|
|
@@ -90,14 +90,23 @@ if [ -f "$BASELINE_EP" ]; then
|
|
|
n_rmv=$(echo "$RMVEP" | grep -c . || true)
|
|
|
log "接口面变化:新增 $n_new 个,移除 $n_rmv 个"
|
|
|
if [ "$n_new" -gt 0 ]; then
|
|
|
- : > "$EXTRA_CASES"
|
|
|
+ # 合并而非截断:旧实现 `: > "$EXTRA_CASES"` 会先清空整个文件再只写「本轮新增」,
|
|
|
+ # 导致上一轮自动补全的用例被静默丢弃(每出现 1 个新接口就抹掉上一批,持续性漏测)。
|
|
|
+ # 改为与既有内容取并集去重后写回。
|
|
|
+ _EXTRA_TMP="$EXTRA_CASES.tmp.$$"
|
|
|
+ : > "$_EXTRA_TMP"
|
|
|
+ [ -f "$EXTRA_CASES" ] && cat "$EXTRA_CASES" >> "$_EXTRA_TMP"
|
|
|
while IFS= read -r line; do
|
|
|
[ -z "$line" ] && continue
|
|
|
m=$(echo "$line" | awk '{print $1}')
|
|
|
p=$(echo "$line" | awk '{print $2}')
|
|
|
- printf 'auto\tauto\t%s\t%s\tfalse\tfalse\n' "$m" "$p" >> "$EXTRA_CASES"
|
|
|
+ printf 'auto\tauto\t%s\t%s\tfalse\tfalse\n' "$m" "$p" >> "$_EXTRA_TMP"
|
|
|
done <<< "$NEWEP"
|
|
|
- log "已为新增接口生成补全用例 -> $EXTRA_CASES"
|
|
|
+ # 按 method+path 去重排序后回写
|
|
|
+ sort -u -t$'\t' -k3,4 "$_EXTRA_TMP" > "$EXTRA_CASES"
|
|
|
+ rm -f "$_EXTRA_TMP"
|
|
|
+ _extra_n=$(grep -c . "$EXTRA_CASES" 2>/dev/null || echo 0)
|
|
|
+ log "已为新增接口生成补全用例(并集去重后共 $_extra_n 条)-> $EXTRA_CASES"
|
|
|
fi
|
|
|
else
|
|
|
log "首次运行:建立接口基线(不触发补全)"
|
|
|
@@ -324,24 +333,47 @@ for f in specs:
|
|
|
completed_runnable = e2e_total - skip
|
|
|
|
|
|
# 3) API e2e 末次实跑(.last-run.json)
|
|
|
+# 注意:.last-run.json 只含 {"status","failedTests"},不含 passed 数,
|
|
|
+# 旧实现用 j.get('passed', ...) 永远取不到,导致报告长期显示「末次 0 通过」。
|
|
|
+# 改为优先解析 playwright_smoke.log 的汇总行("<n> passed"),并以 .last-run.json 兜底。
|
|
|
api_pass = api_fixme = 0
|
|
|
-lr = os.path.join(tests_dir, 'test-results', '.last-run.json')
|
|
|
-if os.path.exists(lr):
|
|
|
+api_pass_src = '未取到结果'
|
|
|
+_pw_log = os.path.join(repo_dir, 'playwright_smoke.log')
|
|
|
+if os.path.exists(_pw_log):
|
|
|
try:
|
|
|
- j = json.load(open(lr, encoding='utf-8'))
|
|
|
- if isinstance(j, dict):
|
|
|
- api_pass = int(j.get('passed', j.get('stats', {}).get('expected', 0) or 0))
|
|
|
- api_fixme = int(j.get('fixme', j.get('stats', {}).get('skipped', 0) or 0))
|
|
|
+ _txt = open(_pw_log, encoding='utf-8', errors='ignore').read()
|
|
|
+ _m = re.findall(r'(\d+)\s+passed', _txt)
|
|
|
+ _s = re.findall(r'(\d+)\s+skipped', _txt)
|
|
|
+ if _m:
|
|
|
+ api_pass = int(_m[-1])
|
|
|
+ api_fixme = int(_s[-1]) if _s else 0
|
|
|
+ api_pass_src = 'playwright_smoke.log'
|
|
|
except Exception:
|
|
|
pass
|
|
|
-
|
|
|
-# 4) NOT_FOUND 端点数(从本轮 CUR_TSV 直接统计,避免依赖 env 未导出字段)
|
|
|
+if api_pass_src != 'playwright_smoke.log':
|
|
|
+ lr = os.path.join(tests_dir, 'test-results', '.last-run.json')
|
|
|
+ if os.path.exists(lr):
|
|
|
+ try:
|
|
|
+ j = json.load(open(lr, encoding='utf-8'))
|
|
|
+ if isinstance(j, dict):
|
|
|
+ api_pass = int(j.get('passed', j.get('stats', {}).get('expected', 0) or 0))
|
|
|
+ api_fixme = int(j.get('fixme', j.get('stats', {}).get('skipped', 0) or 0))
|
|
|
+ api_pass_src = '.last-run.json'
|
|
|
+ except Exception:
|
|
|
+ pass
|
|
|
+
|
|
|
+# 3b) NOT_FOUND 唯一端点数(按 method+path 去重)
|
|
|
+# CUR_TSV 中同一端点会因多调用点重复出现(历史实测 6 行 -> 实际 2 个唯一端点),
|
|
|
+# 旧实现按行数统计,导致报告写「6 个 NOT_FOUND 端点」而摘要写 2,前后矛盾。
|
|
|
nf = 0
|
|
|
+nf_unique = set()
|
|
|
try:
|
|
|
for line in open(cur_tsv, encoding='utf-8', errors='ignore'):
|
|
|
c = line.rstrip('\n').split('\t')
|
|
|
if len(c) >= 7 and c[6].strip() == 'NOT_FOUND':
|
|
|
nf += 1
|
|
|
+ nf_unique.add((c[2].strip(), c[3].strip()))
|
|
|
+ nf = len(nf_unique)
|
|
|
except Exception:
|
|
|
pass
|
|
|
|
|
|
@@ -354,7 +386,7 @@ if dom > 0:
|
|
|
if skip > 0:
|
|
|
reasons.append((skip, f"已知缺陷/待上线导致跳过:{skip} 个用例被 test.skip/test.fixme 标记(能量发放 500-CFC-EN-001、昵称未持久化-CFC-FM-001 等),待后端修复后启用。"))
|
|
|
if nf > 0:
|
|
|
- reasons.append((nf, f"新功能待部署后验证:接口冒烟发现 {nf} 个 NOT_FOUND 端点,对应前端页面尚未部署到测试环境,相关 E2E 暂挂起。"))
|
|
|
+ reasons.append((nf, f"接口冒烟发现 {nf} 个唯一端点返回 404(NOT_FOUND):前后端契约路径不一致(如前端 child/* 与后端 member/*),需开发对齐路径或补别名后重部署,相关 E2E 暂挂起。"))
|
|
|
if TOTAL_DOC > 0:
|
|
|
reasons.append((TOTAL_DOC, f"文档化用例未全量自动化:{TOTAL_DOC} 条设计用例(TC-xxxx),核心主链路已落地 e2e,边缘/异常流仍以文档形式待补自动化。"))
|
|
|
|
|
|
@@ -363,7 +395,7 @@ section.append("\n## 测试用例完成情况")
|
|
|
section.append(f"- 自动化用例总数(可执行 `test()`):**{e2e_total}**(api 层 {api} / 角色分包 {roles} / DOM 层 {dom})")
|
|
|
section.append(f"- 文档化用例(设计层 TC-xxxx):**{TOTAL_DOC}**")
|
|
|
section.append(f"- 已完成(自动化且未被跳过):**{completed_runnable}/{e2e_total} = {pct(completed_runnable, e2e_total)}**")
|
|
|
-section.append(f"- 已实跑通过:接口冒烟 BIZ_OK **{OK_COUNT}** 端点 + API e2e 末次 **{api_pass}** 通过 / {api_fixme} fixme")
|
|
|
+section.append(f"- 已实跑通过:接口冒烟 BIZ_OK **{OK_COUNT}** 端点 + API e2e 末次 **{api_pass}** 通过 / {api_fixme} skipped(来源 {api_pass_src})")
|
|
|
section.append("")
|
|
|
section.append(f"### 未完成 / 未开始的原因(共 {len(reasons)} 类)")
|
|
|
if reasons:
|
|
|
@@ -438,16 +470,33 @@ if [ "${#PW_RUN[@]}" -gt 0 ] && [ -f "$PW_CFG" ]; then
|
|
|
PW_PASS=$(grep -oE '[0-9]+ passed' "$PW_LOG" | grep -oE '[0-9]+' | head -1)
|
|
|
PW_FAIL=$(grep -oE '[0-9]+ failed' "$PW_LOG" | grep -oE '[0-9]+' | head -1)
|
|
|
PW_SKIP=$(grep -oE '[0-9]+ skipped' "$PW_LOG" | grep -oE '[0-9]+' | head -1)
|
|
|
+ # 崩溃保护:若日志里没有任何用例汇总行(如被沙箱 safe-delete/ETIMEDOUT 打断),
|
|
|
+ # 旧实现会因 grep 取空而回落成「0 passed / 0 failed」,看上去像「全绿」,实为「根本没跑」。
|
|
|
+ # 此时显式标记为「未执行」,避免误报。
|
|
|
+ PW_CRASHED=0
|
|
|
+ if ! grep -qE '[0-9]+ (passed|failed|skipped)' "$PW_LOG"; then
|
|
|
+ PW_CRASHED=1
|
|
|
+ fi
|
|
|
{
|
|
|
echo ""
|
|
|
echo "## Playwright E2E 冒烟(api 层,独立,不阻断主闭环)"
|
|
|
echo "- 执行器: ${PW_RUN[*]} test -c playwright.config.api.js --project=desktop"
|
|
|
- echo "- 结果: **${PW_PASS:-0} passed / ${PW_FAIL:-0} failed / ${PW_SKIP:-0} skipped**"
|
|
|
+ if [ "$PW_CRASHED" = "1" ]; then
|
|
|
+ echo "- 结果: **未执行(执行器异常中断,日志无用例汇总行)**"
|
|
|
+ echo "- ⚠️ 本轮 E2E 未产出结果,**不可**解读为 0 failed / 全绿;需人工重跑确认。"
|
|
|
+ echo "- 失败特征: $(grep -oE 'Error: [^\\n]*' "$PW_LOG" | head -1)"
|
|
|
+ else
|
|
|
+ echo "- 结果: **${PW_PASS:-0} passed / ${PW_FAIL:-0} failed / ${PW_SKIP:-0} skipped**"
|
|
|
+ fi
|
|
|
echo "- 覆盖: tests/e2e/api/*.spec.js(含 nonfunc-security 安全契约、nonfunc-perf-compat 性能兼容)"
|
|
|
echo "- 说明: 红用例=已知后端缺口(权限提升/开放端点/上传未校验/缺鉴权过滤器),作持续告警;失败不阻断本闭环。"
|
|
|
echo "- 明细: $PW_LOG"
|
|
|
} > "$PW_RPT"
|
|
|
- log "Playwright E2E 冒烟完成: ${PW_PASS:-0} passed / ${PW_FAIL:-0} failed / ${PW_SKIP:-0} skipped(报告 $PW_RPT)"
|
|
|
+ if [ "$PW_CRASHED" = "1" ]; then
|
|
|
+ log "⚠️ Playwright E2E 冒烟未产出结果(执行器异常中断,非 0 failed)—— 报告已标注「未执行」,不可解读为全绿"
|
|
|
+ else
|
|
|
+ log "Playwright E2E 冒烟完成: ${PW_PASS:-0} passed / ${PW_FAIL:-0} failed / ${PW_SKIP:-0} skipped(报告 $PW_RPT)"
|
|
|
+ fi
|
|
|
else
|
|
|
log "⚠️ 未找到 Playwright 执行器或配置,跳过 E2E 冒烟"
|
|
|
fi
|