- 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 产物装配与部署形态。
69 lines
2.9 KiB
JavaScript
69 lines
2.9 KiB
JavaScript
// 从 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)}`);
|