forked from baron/baron-sso
userfront&backend test coverage 추가
This commit is contained in:
115
scripts/summarize_flutter_coverage.mjs
Normal file
115
scripts/summarize_flutter_coverage.mjs
Normal file
@@ -0,0 +1,115 @@
|
||||
import { mkdir, readFile, writeFile } from "node:fs/promises";
|
||||
import path from "node:path";
|
||||
|
||||
const repoRoot = process.cwd();
|
||||
const packageName = process.argv[2] || "userfront";
|
||||
const lcovPath = path.join(repoRoot, packageName, "coverage", "lcov.info");
|
||||
const reportsDir = path.join(repoRoot, "reports");
|
||||
|
||||
const excludedSourceFiles = new Set(["lib/main.dart", "lib/i18n_data.dart"]);
|
||||
|
||||
function normalizeSourcePath(sourcePath) {
|
||||
const normalized = sourcePath.replace(/\\/g, "/");
|
||||
const packagePrefix = `${packageName}/`;
|
||||
return normalized.startsWith(packagePrefix)
|
||||
? normalized.slice(packagePrefix.length)
|
||||
: normalized;
|
||||
}
|
||||
|
||||
function shouldExcludeSource(sourcePath) {
|
||||
const normalized = normalizeSourcePath(sourcePath);
|
||||
return (
|
||||
excludedSourceFiles.has(normalized) ||
|
||||
normalized.endsWith(".g.dart") ||
|
||||
normalized.endsWith(".freezed.dart")
|
||||
);
|
||||
}
|
||||
|
||||
function parseLcov(raw) {
|
||||
const records = [];
|
||||
let current = null;
|
||||
|
||||
for (const line of raw.split(/\r?\n/)) {
|
||||
if (line.startsWith("SF:")) {
|
||||
current = { sourceFile: normalizeSourcePath(line.slice(3)), lines: [] };
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!current) continue;
|
||||
|
||||
if (line.startsWith("DA:")) {
|
||||
const [, hitsValue] = line.slice(3).split(",");
|
||||
const hits = Number(hitsValue);
|
||||
current.lines.push(Number.isFinite(hits) ? hits : 0);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (line === "end_of_record") {
|
||||
records.push(current);
|
||||
current = null;
|
||||
}
|
||||
}
|
||||
|
||||
if (current) {
|
||||
records.push(current);
|
||||
}
|
||||
|
||||
return records;
|
||||
}
|
||||
|
||||
function formatPct(value) {
|
||||
return `${value.toFixed(2)}%`;
|
||||
}
|
||||
|
||||
function renderMarkdown(row) {
|
||||
return [
|
||||
"# Userfront Flutter Coverage Summary",
|
||||
"",
|
||||
"| Package | Lines | Covered / Total | LCOV |",
|
||||
"| --- | ---: | ---: | --- |",
|
||||
`| ${row.package} | ${formatPct(row.lines)} | ${row.coveredLines} / ${row.totalLines} | ${row.lcovPath} |`,
|
||||
"",
|
||||
"Coverage excludes Flutter bootstrap/generated files: `lib/main.dart`, `lib/i18n_data.dart`, `*.g.dart`, `*.freezed.dart`.",
|
||||
"",
|
||||
].join("\n");
|
||||
}
|
||||
|
||||
const lcov = await readFile(lcovPath, "utf8");
|
||||
const includedRecords = parseLcov(lcov).filter(
|
||||
(record) => !shouldExcludeSource(record.sourceFile),
|
||||
);
|
||||
|
||||
const totalLines = includedRecords.reduce(
|
||||
(total, record) => total + record.lines.length,
|
||||
0,
|
||||
);
|
||||
const coveredLines = includedRecords.reduce(
|
||||
(total, record) => total + record.lines.filter((hits) => hits > 0).length,
|
||||
0,
|
||||
);
|
||||
const lineCoverage = totalLines === 0 ? 0 : (coveredLines / totalLines) * 100;
|
||||
|
||||
const row = {
|
||||
package: packageName,
|
||||
statements: lineCoverage,
|
||||
branches: null,
|
||||
functions: null,
|
||||
lines: lineCoverage,
|
||||
coveredLines,
|
||||
totalLines,
|
||||
summaryPath: "reports/package-coverage-summary.json",
|
||||
htmlPath: null,
|
||||
lcovPath: `${packageName}/coverage/lcov.info`,
|
||||
};
|
||||
|
||||
await mkdir(reportsDir, { recursive: true });
|
||||
await writeFile(
|
||||
path.join(reportsDir, "package-coverage-summary.json"),
|
||||
`${JSON.stringify({ packages: [row] }, null, 2)}\n`,
|
||||
);
|
||||
await writeFile(
|
||||
path.join(reportsDir, `${packageName}-coverage-summary.md`),
|
||||
renderMarkdown(row),
|
||||
);
|
||||
|
||||
console.log(renderMarkdown(row));
|
||||
@@ -94,7 +94,8 @@ function colorForParts(parts) {
|
||||
const normalized = parts.map(normalizeResult);
|
||||
if (normalized.includes("failure")) return resultStyles.failure.color;
|
||||
if (normalized.includes("cancelled")) return resultStyles.cancelled.color;
|
||||
if (normalized.every((part) => part === "success")) return resultStyles.success.color;
|
||||
if (normalized.every((part) => part === "success"))
|
||||
return resultStyles.success.color;
|
||||
return resultStyles.unknown.color;
|
||||
}
|
||||
|
||||
@@ -161,7 +162,14 @@ async function findCoverageSummaries(directory) {
|
||||
|
||||
for (const entry of entries) {
|
||||
const entryPath = path.join(directory, entry.name);
|
||||
if (entry.isFile() && entry.name === "vitest-coverage-summary.json") {
|
||||
if (
|
||||
entry.isFile() &&
|
||||
[
|
||||
"backend-coverage-summary.json",
|
||||
"package-coverage-summary.json",
|
||||
"vitest-coverage-summary.json",
|
||||
].includes(entry.name)
|
||||
) {
|
||||
results.push(entryPath);
|
||||
continue;
|
||||
}
|
||||
@@ -216,7 +224,13 @@ function coveragePart(result, statements) {
|
||||
};
|
||||
}
|
||||
|
||||
function updatePackageBadge(manifest, key, testResult, coverageResult, statements) {
|
||||
function updatePackageBadge(
|
||||
manifest,
|
||||
key,
|
||||
testResult,
|
||||
coverageResult,
|
||||
statements,
|
||||
) {
|
||||
if (!badgeDefinitions[key]) return;
|
||||
const test = normalizeResult(testResult);
|
||||
const coverage = coveragePart(coverageResult, statements);
|
||||
@@ -224,13 +238,14 @@ function updatePackageBadge(manifest, key, testResult, coverageResult, statement
|
||||
...(manifest.badges[key] ?? badgeDefinitions[key]),
|
||||
label: badgeDefinitions[key].label,
|
||||
message: `${compactResult(test)} | ${coverage.message}`,
|
||||
color: test === "failure" || coverage.result === "failure"
|
||||
? resultStyles.failure.color
|
||||
: test === "cancelled" || coverage.result === "cancelled"
|
||||
? resultStyles.cancelled.color
|
||||
: coverage.result === "success"
|
||||
? coverage.color
|
||||
: colorForParts([test, coverage.result]),
|
||||
color:
|
||||
test === "failure" || coverage.result === "failure"
|
||||
? resultStyles.failure.color
|
||||
: test === "cancelled" || coverage.result === "cancelled"
|
||||
? resultStyles.cancelled.color
|
||||
: coverage.result === "success"
|
||||
? coverage.color
|
||||
: colorForParts([test, coverage.result]),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -332,6 +347,7 @@ const browserResults = {
|
||||
|
||||
const legacyCoverageResult = process.env.COVERAGE_RESULT;
|
||||
const coverageJobResults = {
|
||||
backend: process.env.BACKEND_COVERAGE_RESULT,
|
||||
userfront: process.env.USERFRONT_COVERAGE_RESULT,
|
||||
adminfront: process.env.ADMINFRONT_COVERAGE_RESULT || legacyCoverageResult,
|
||||
devfront: process.env.DEVFRONT_COVERAGE_RESULT || legacyCoverageResult,
|
||||
@@ -367,7 +383,6 @@ if (process.env.BADGE_UPDATE_CODE_CHECK !== "false") {
|
||||
}
|
||||
|
||||
updateResultBadge(manifest, "biome", jobResults.biome);
|
||||
updateCompactResultBadge(manifest, "backend-tests", jobResults.backend);
|
||||
|
||||
const coverageSummaries = process.env.COVERAGE_SUMMARY_PATH
|
||||
? [process.env.COVERAGE_SUMMARY_PATH]
|
||||
@@ -378,6 +393,21 @@ for (const summaryPath of coverageSummaries) {
|
||||
for (const row of coverageSummary?.packages ?? []) {
|
||||
coverageByPackage.set(row.package, row.statements);
|
||||
}
|
||||
if (coverageSummary?.package) {
|
||||
coverageByPackage.set(coverageSummary.package, coverageSummary.statements);
|
||||
}
|
||||
}
|
||||
|
||||
if (coverageJobResults.backend) {
|
||||
updatePackageBadge(
|
||||
manifest,
|
||||
"backend-tests",
|
||||
jobResults.backend,
|
||||
coverageJobResults.backend,
|
||||
coverageByPackage.get("backend"),
|
||||
);
|
||||
} else {
|
||||
updateCompactResultBadge(manifest, "backend-tests", jobResults.backend);
|
||||
}
|
||||
|
||||
for (const [key, testResult, coverageResult] of [
|
||||
|
||||
Reference in New Issue
Block a user