chore(qa): 验收台账与证据入库 + 构建/部署配置同步
- docs/acceptance/qa-tracker.md:跨周期缺陷单一真源台账(§7=第五轮)。 - 周期 1/2 + iPhone SE/axe 验收证据目录、ACCEPTANCE_REVIEW 快照入库。 - 同步 README/CONTEXT/CLAUDE/DESIGN/testing/deployment/lessons-learned 口径; next.config/Dockerfile/nginx/Jenkinsfile/docker-compose/sentry/prisma 对齐 standalone 产物装配与部署形态。
This commit is contained in:
@@ -0,0 +1,68 @@
|
||||
// 从 chain6 的 allure-results 里取出「本轮真正跑过的用例」按状态分组,
|
||||
// 目的:给 ② 的 28 skipped 一份**具名**证据(哪些用例、在哪个 project、以什么理由跳过),
|
||||
// 而不是只引用汇总行里的计数。汇总计数只能证明「数量」,证明不了「没有把失败洗成跳过」。
|
||||
import { readdirSync, statSync, readFileSync, writeFileSync } from 'fs';
|
||||
import { resolve } from 'path';
|
||||
|
||||
const DIR = resolve(process.cwd() ?? '.', 'e2e/allure-results');
|
||||
// chain6 功能阶段:03:52:08Z → 04:20:43Z;视觉阶段紧接其后。留出余量按 UTC 当日 03:50–04:25 过滤。
|
||||
const FROM = Date.parse('2026-09-22T03:50:00Z');
|
||||
const TO = Date.parse('2026-09-22T04:25:00Z');
|
||||
|
||||
const files = readdirSync(DIR).filter((f) => f.endsWith('-result.json'));
|
||||
const rows = [];
|
||||
for (const f of files) {
|
||||
const p = resolve(DIR, f);
|
||||
const m = statSync(p).mtimeMs;
|
||||
if (m < FROM || m > TO) continue;
|
||||
let j;
|
||||
try {
|
||||
j = JSON.parse(readFileSync(p, 'utf8'));
|
||||
} catch {
|
||||
continue;
|
||||
}
|
||||
const labels = {};
|
||||
for (const l of j.labels || []) labels[l.name] = l.value;
|
||||
rows.push({
|
||||
file: f,
|
||||
mtime: new Date(m).toISOString(),
|
||||
status: j.status,
|
||||
reason: j.statusDetails?.message || j.statusDetails?.trace || '',
|
||||
project: labels.device || labels.suiteParent || '',
|
||||
suite: labels.suite || '',
|
||||
fullName: j.fullName || '',
|
||||
name: j.name || '',
|
||||
});
|
||||
}
|
||||
|
||||
const byStatus = {};
|
||||
for (const r of rows) byStatus[r.status] = (byStatus[r.status] || 0) + 1;
|
||||
const skipped = rows.filter((r) => r.status === 'skipped').sort((a, b) => (a.fullName + a.project).localeCompare(b.fullName + b.project));
|
||||
|
||||
const out = {
|
||||
generatedAt: new Date().toISOString(),
|
||||
source: 'e2e/allure-results(chain6 最终代码树 `npm run test` 功能+视觉两阶段的 allure 落盘)',
|
||||
window: { from: new Date(FROM).toISOString(), to: new Date(TO).toISOString() },
|
||||
totalResults: rows.length,
|
||||
byStatus,
|
||||
skippedCount: skipped.length,
|
||||
// 按 (suite, reason) 聚合,便于核对「每处 test.skip 条件命中了多少条」
|
||||
skipTally: Object.entries(
|
||||
skipped.reduce((acc, r) => {
|
||||
const k = `${r.suite} :: ${r.reason.slice(0, 80)}`;
|
||||
acc[k] = (acc[k] || 0) + 1;
|
||||
return acc;
|
||||
}, {}),
|
||||
).map(([k, n]) => ({ group: k, count: n })),
|
||||
skipped,
|
||||
};
|
||||
writeFileSync(
|
||||
resolve(process.cwd() ?? '.', 'docs/acceptance/2026-09-21-gates/skipped-tests-final-tree.json'),
|
||||
JSON.stringify(out, null, 2),
|
||||
);
|
||||
console.log(`window results=${rows.length} byStatus=${JSON.stringify(byStatus)}`);
|
||||
console.log(`skipped=${skipped.length}`);
|
||||
for (const t of out.skipTally) console.log(` x${t.count} ${t.group}`);
|
||||
const nonPass = rows.filter((r) => r.status !== 'passed' && r.status !== 'skipped');
|
||||
console.log(`otherNonPassed=${nonPass.length}`);
|
||||
for (const r of nonPass.slice(0, 10)) console.log(` ${r.status} ${r.project} ${r.fullName} :: ${r.reason.slice(0, 120)}`);
|
||||
Reference in New Issue
Block a user