mirror of
https://github.com/langgenius/dify.git
synced 2026-03-30 02:20:16 +08:00
chore: update coverage summary check in web tests workflow (#33533)
Co-authored-by: CodingOnStar <hanxujiang@dify.com> Co-authored-by: yyh <92089059+lyzno1@users.noreply.github.com>
This commit is contained in:
committed by
CodingOnStar
parent
75a9939094
commit
fd100a868d
@ -6,41 +6,34 @@ import {
|
||||
getChangedBranchCoverage,
|
||||
getChangedStatementCoverage,
|
||||
getIgnoredChangedLinesFromFile,
|
||||
getLineHits,
|
||||
normalizeToRepoRelative,
|
||||
parseChangedLineMap,
|
||||
} from './check-components-diff-coverage-lib.mjs'
|
||||
import { COMPONENT_COVERAGE_EXCLUDE_LABEL } from './component-coverage-filters.mjs'
|
||||
import {
|
||||
collectComponentCoverageExcludedFiles,
|
||||
COMPONENT_COVERAGE_EXCLUDE_LABEL,
|
||||
} from './component-coverage-filters.mjs'
|
||||
import {
|
||||
COMPONENTS_GLOBAL_THRESHOLDS,
|
||||
EXCLUDED_COMPONENT_MODULES,
|
||||
getComponentModuleThreshold,
|
||||
} from './components-coverage-thresholds.mjs'
|
||||
APP_COMPONENTS_PREFIX,
|
||||
createComponentCoverageContext,
|
||||
getModuleName,
|
||||
isAnyComponentSourceFile,
|
||||
isExcludedComponentSourceFile,
|
||||
isTrackedComponentSourceFile,
|
||||
loadTrackedCoverageEntries,
|
||||
} from './components-coverage-common.mjs'
|
||||
import { EXCLUDED_COMPONENT_MODULES } from './components-coverage-thresholds.mjs'
|
||||
|
||||
const APP_COMPONENTS_PREFIX = 'web/app/components/'
|
||||
const APP_COMPONENTS_COVERAGE_PREFIX = 'app/components/'
|
||||
const SHARED_TEST_PREFIX = 'web/__tests__/'
|
||||
const STRICT_TEST_FILE_TOUCH = process.env.STRICT_COMPONENT_TEST_TOUCH === 'true'
|
||||
const EXCLUDED_MODULES_LABEL = [...EXCLUDED_COMPONENT_MODULES].sort().join(', ')
|
||||
const DIFF_RANGE_MODE = process.env.DIFF_RANGE_MODE === 'exact' ? 'exact' : 'merge-base'
|
||||
const EXCLUDED_MODULES_LABEL = [...EXCLUDED_COMPONENT_MODULES].sort().join(', ')
|
||||
|
||||
const repoRoot = repoRootFromCwd()
|
||||
const webRoot = path.join(repoRoot, 'web')
|
||||
const excludedComponentCoverageFiles = new Set(
|
||||
collectComponentCoverageExcludedFiles(path.join(webRoot, 'app/components'), { pathPrefix: 'web/app/components' }),
|
||||
)
|
||||
const context = createComponentCoverageContext(repoRoot)
|
||||
const baseSha = process.env.BASE_SHA?.trim()
|
||||
const headSha = process.env.HEAD_SHA?.trim() || 'HEAD'
|
||||
const coverageFinalPath = path.join(webRoot, 'coverage', 'coverage-final.json')
|
||||
const coverageFinalPath = path.join(context.webRoot, 'coverage', 'coverage-final.json')
|
||||
|
||||
if (!baseSha || /^0+$/.test(baseSha)) {
|
||||
appendSummary([
|
||||
'### app/components Diff Coverage',
|
||||
'### app/components Pure Diff Coverage',
|
||||
'',
|
||||
'Skipped diff coverage check because `BASE_SHA` was not available.',
|
||||
'Skipped pure diff coverage check because `BASE_SHA` was not available.',
|
||||
])
|
||||
process.exit(0)
|
||||
}
|
||||
@ -53,52 +46,27 @@ if (!fs.existsSync(coverageFinalPath)) {
|
||||
const coverage = JSON.parse(fs.readFileSync(coverageFinalPath, 'utf8'))
|
||||
const changedFiles = getChangedFiles(baseSha, headSha)
|
||||
const changedComponentSourceFiles = changedFiles.filter(isAnyComponentSourceFile)
|
||||
const changedSourceFiles = changedComponentSourceFiles.filter(isTrackedComponentSourceFile)
|
||||
const changedExcludedSourceFiles = changedComponentSourceFiles.filter(isExcludedComponentSourceFile)
|
||||
const changedTestFiles = changedFiles.filter(isRelevantTestFile)
|
||||
const changedSourceFiles = changedComponentSourceFiles.filter(filePath => isTrackedComponentSourceFile(filePath, context.excludedComponentCoverageFiles))
|
||||
const changedExcludedSourceFiles = changedComponentSourceFiles.filter(filePath => isExcludedComponentSourceFile(filePath, context.excludedComponentCoverageFiles))
|
||||
|
||||
if (changedSourceFiles.length === 0) {
|
||||
appendSummary(buildSkipSummary(changedExcludedSourceFiles))
|
||||
process.exit(0)
|
||||
}
|
||||
|
||||
const coverageEntries = new Map()
|
||||
for (const [file, entry] of Object.entries(coverage)) {
|
||||
const repoRelativePath = normalizeToRepoRelative(entry.path ?? file, {
|
||||
appComponentsCoveragePrefix: APP_COMPONENTS_COVERAGE_PREFIX,
|
||||
appComponentsPrefix: APP_COMPONENTS_PREFIX,
|
||||
repoRoot,
|
||||
sharedTestPrefix: SHARED_TEST_PREFIX,
|
||||
webRoot,
|
||||
})
|
||||
if (!isTrackedComponentSourceFile(repoRelativePath))
|
||||
continue
|
||||
|
||||
coverageEntries.set(repoRelativePath, entry)
|
||||
}
|
||||
|
||||
const fileCoverageRows = []
|
||||
const moduleCoverageMap = new Map()
|
||||
|
||||
for (const [file, entry] of coverageEntries.entries()) {
|
||||
const stats = getCoverageStats(entry)
|
||||
const moduleName = getModuleName(file)
|
||||
fileCoverageRows.push({ file, moduleName, ...stats })
|
||||
mergeCoverageStats(moduleCoverageMap, moduleName, stats)
|
||||
}
|
||||
|
||||
const overallCoverage = sumCoverageStats(fileCoverageRows)
|
||||
const coverageEntries = loadTrackedCoverageEntries(coverage, context)
|
||||
const diffChanges = getChangedLineMap(baseSha, headSha)
|
||||
const diffRows = []
|
||||
const ignoredDiffLines = []
|
||||
const invalidIgnorePragmas = []
|
||||
|
||||
for (const [file, changedLines] of diffChanges.entries()) {
|
||||
if (!isTrackedComponentSourceFile(file))
|
||||
if (!isTrackedComponentSourceFile(file, context.excludedComponentCoverageFiles))
|
||||
continue
|
||||
|
||||
const entry = coverageEntries.get(file)
|
||||
const ignoreInfo = getIgnoredChangedLinesFromFile(path.join(repoRoot, file), changedLines)
|
||||
|
||||
for (const [line, reason] of ignoreInfo.ignoredLines.entries()) {
|
||||
ignoredDiffLines.push({
|
||||
file,
|
||||
@ -106,6 +74,7 @@ for (const [file, changedLines] of diffChanges.entries()) {
|
||||
reason,
|
||||
})
|
||||
}
|
||||
|
||||
for (const invalidPragma of ignoreInfo.invalidPragmas) {
|
||||
invalidIgnorePragmas.push({
|
||||
file,
|
||||
@ -137,40 +106,15 @@ const diffTotals = diffRows.reduce((acc, row) => {
|
||||
|
||||
const diffStatementFailures = diffRows.filter(row => row.statements.uncoveredLines.length > 0)
|
||||
const diffBranchFailures = diffRows.filter(row => row.branches.uncoveredBranches.length > 0)
|
||||
const overallThresholdFailures = getThresholdFailures(overallCoverage, COMPONENTS_GLOBAL_THRESHOLDS)
|
||||
const moduleCoverageRows = [...moduleCoverageMap.entries()]
|
||||
.map(([moduleName, stats]) => ({
|
||||
moduleName,
|
||||
stats,
|
||||
thresholds: getComponentModuleThreshold(moduleName),
|
||||
}))
|
||||
.map(row => ({
|
||||
...row,
|
||||
failures: row.thresholds ? getThresholdFailures(row.stats, row.thresholds) : [],
|
||||
}))
|
||||
const moduleThresholdFailures = moduleCoverageRows
|
||||
.filter(row => row.failures.length > 0)
|
||||
.flatMap(row => row.failures.map(failure => ({
|
||||
moduleName: row.moduleName,
|
||||
...failure,
|
||||
})))
|
||||
const hasRelevantTestChanges = changedTestFiles.length > 0
|
||||
const missingTestTouch = !hasRelevantTestChanges
|
||||
|
||||
appendSummary(buildSummary({
|
||||
overallCoverage,
|
||||
overallThresholdFailures,
|
||||
moduleCoverageRows,
|
||||
moduleThresholdFailures,
|
||||
changedSourceFiles,
|
||||
diffBranchFailures,
|
||||
diffRows,
|
||||
diffStatementFailures,
|
||||
diffTotals,
|
||||
changedSourceFiles,
|
||||
changedTestFiles,
|
||||
ignoredDiffLines,
|
||||
invalidIgnorePragmas,
|
||||
missingTestTouch,
|
||||
}))
|
||||
|
||||
if (process.env.CI) {
|
||||
@ -178,44 +122,37 @@ if (process.env.CI) {
|
||||
const firstLine = failure.statements.uncoveredLines[0] ?? 1
|
||||
console.log(`::error file=${failure.file},line=${firstLine}::Uncovered changed statements: ${formatLineRanges(failure.statements.uncoveredLines)}`)
|
||||
}
|
||||
|
||||
for (const failure of diffBranchFailures.slice(0, 20)) {
|
||||
const firstBranch = failure.branches.uncoveredBranches[0]
|
||||
const line = firstBranch?.line ?? 1
|
||||
console.log(`::error file=${failure.file},line=${line}::Uncovered changed branches: ${formatBranchRefs(failure.branches.uncoveredBranches)}`)
|
||||
}
|
||||
|
||||
for (const invalidPragma of invalidIgnorePragmas.slice(0, 20)) {
|
||||
console.log(`::error file=${invalidPragma.file},line=${invalidPragma.line}::Invalid diff coverage ignore pragma: ${invalidPragma.reason}`)
|
||||
}
|
||||
}
|
||||
|
||||
if (
|
||||
overallThresholdFailures.length > 0
|
||||
|| moduleThresholdFailures.length > 0
|
||||
|| diffStatementFailures.length > 0
|
||||
diffStatementFailures.length > 0
|
||||
|| diffBranchFailures.length > 0
|
||||
|| invalidIgnorePragmas.length > 0
|
||||
|| (STRICT_TEST_FILE_TOUCH && missingTestTouch)
|
||||
) {
|
||||
process.exit(1)
|
||||
}
|
||||
|
||||
function buildSummary({
|
||||
overallCoverage,
|
||||
overallThresholdFailures,
|
||||
moduleCoverageRows,
|
||||
moduleThresholdFailures,
|
||||
changedSourceFiles,
|
||||
diffBranchFailures,
|
||||
diffRows,
|
||||
diffStatementFailures,
|
||||
diffTotals,
|
||||
changedSourceFiles,
|
||||
changedTestFiles,
|
||||
ignoredDiffLines,
|
||||
invalidIgnorePragmas,
|
||||
missingTestTouch,
|
||||
}) {
|
||||
const lines = [
|
||||
'### app/components Diff Coverage',
|
||||
'### app/components Pure Diff Coverage',
|
||||
'',
|
||||
`Compared \`${baseSha.slice(0, 12)}\` -> \`${headSha.slice(0, 12)}\``,
|
||||
`Diff range mode: \`${DIFF_RANGE_MODE}\``,
|
||||
@ -225,60 +162,11 @@ function buildSummary({
|
||||
'',
|
||||
'| Check | Result | Details |',
|
||||
'|---|---:|---|',
|
||||
`| Overall tracked lines | ${formatPercent(overallCoverage.lines)} | ${overallCoverage.lines.covered}/${overallCoverage.lines.total}; threshold ${COMPONENTS_GLOBAL_THRESHOLDS.lines}% |`,
|
||||
`| Overall tracked statements | ${formatPercent(overallCoverage.statements)} | ${overallCoverage.statements.covered}/${overallCoverage.statements.total}; threshold ${COMPONENTS_GLOBAL_THRESHOLDS.statements}% |`,
|
||||
`| Overall tracked functions | ${formatPercent(overallCoverage.functions)} | ${overallCoverage.functions.covered}/${overallCoverage.functions.total}; threshold ${COMPONENTS_GLOBAL_THRESHOLDS.functions}% |`,
|
||||
`| Overall tracked branches | ${formatPercent(overallCoverage.branches)} | ${overallCoverage.branches.covered}/${overallCoverage.branches.total}; threshold ${COMPONENTS_GLOBAL_THRESHOLDS.branches}% |`,
|
||||
`| Changed statements | ${formatDiffPercent(diffTotals.statements)} | ${diffTotals.statements.covered}/${diffTotals.statements.total} |`,
|
||||
`| Changed branches | ${formatDiffPercent(diffTotals.branches)} | ${diffTotals.branches.covered}/${diffTotals.branches.total} |`,
|
||||
'',
|
||||
]
|
||||
|
||||
if (overallThresholdFailures.length > 0) {
|
||||
lines.push('Overall thresholds failed:')
|
||||
for (const failure of overallThresholdFailures)
|
||||
lines.push(`- ${failure.metric}: ${failure.actual.toFixed(2)}% < ${failure.expected}%`)
|
||||
lines.push('')
|
||||
}
|
||||
|
||||
if (moduleThresholdFailures.length > 0) {
|
||||
lines.push('Module thresholds failed:')
|
||||
for (const failure of moduleThresholdFailures)
|
||||
lines.push(`- ${failure.moduleName} ${failure.metric}: ${failure.actual.toFixed(2)}% < ${failure.expected}%`)
|
||||
lines.push('')
|
||||
}
|
||||
|
||||
const moduleRows = moduleCoverageRows
|
||||
.map(({ moduleName, stats, thresholds, failures }) => ({
|
||||
moduleName,
|
||||
lines: percentage(stats.lines.covered, stats.lines.total),
|
||||
statements: percentage(stats.statements.covered, stats.statements.total),
|
||||
functions: percentage(stats.functions.covered, stats.functions.total),
|
||||
branches: percentage(stats.branches.covered, stats.branches.total),
|
||||
thresholds,
|
||||
failures,
|
||||
}))
|
||||
.sort((a, b) => {
|
||||
if (a.failures.length !== b.failures.length)
|
||||
return b.failures.length - a.failures.length
|
||||
|
||||
return a.lines - b.lines || a.moduleName.localeCompare(b.moduleName)
|
||||
})
|
||||
|
||||
lines.push('<details><summary>Module coverage</summary>')
|
||||
lines.push('')
|
||||
lines.push('| Module | Lines | Statements | Functions | Branches | Thresholds | Status |')
|
||||
lines.push('|---|---:|---:|---:|---:|---|---|')
|
||||
for (const row of moduleRows) {
|
||||
const thresholdLabel = row.thresholds
|
||||
? `L${row.thresholds.lines}/S${row.thresholds.statements}/F${row.thresholds.functions}/B${row.thresholds.branches}`
|
||||
: 'n/a'
|
||||
const status = row.thresholds ? (row.failures.length > 0 ? 'fail' : 'pass') : 'info'
|
||||
lines.push(`| ${row.moduleName} | ${row.lines.toFixed(2)}% | ${row.statements.toFixed(2)}% | ${row.functions.toFixed(2)}% | ${row.branches.toFixed(2)}% | ${thresholdLabel} | ${status} |`)
|
||||
}
|
||||
lines.push('</details>')
|
||||
lines.push('')
|
||||
|
||||
const changedRows = diffRows
|
||||
.filter(row => row.statements.total > 0 || row.branches.total > 0)
|
||||
.sort((a, b) => {
|
||||
@ -297,59 +185,43 @@ function buildSummary({
|
||||
lines.push('</details>')
|
||||
lines.push('')
|
||||
|
||||
if (missingTestTouch) {
|
||||
lines.push(`Warning: tracked source files changed under \`web/app/components/\`, but no test files changed under \`web/app/components/**\` or \`web/__tests__/\`.`)
|
||||
if (STRICT_TEST_FILE_TOUCH)
|
||||
lines.push('`STRICT_COMPONENT_TEST_TOUCH=true` is enabled, so this warning fails the check.')
|
||||
lines.push('')
|
||||
}
|
||||
else {
|
||||
lines.push(`Relevant test files changed: ${changedTestFiles.length}`)
|
||||
lines.push('')
|
||||
}
|
||||
|
||||
if (diffStatementFailures.length > 0) {
|
||||
lines.push('Uncovered changed statements:')
|
||||
for (const row of diffStatementFailures) {
|
||||
for (const row of diffStatementFailures)
|
||||
lines.push(`- ${row.file.replace('web/', '')}: ${formatLineRanges(row.statements.uncoveredLines)}`)
|
||||
}
|
||||
lines.push('')
|
||||
}
|
||||
|
||||
if (diffBranchFailures.length > 0) {
|
||||
lines.push('Uncovered changed branches:')
|
||||
for (const row of diffBranchFailures) {
|
||||
for (const row of diffBranchFailures)
|
||||
lines.push(`- ${row.file.replace('web/', '')}: ${formatBranchRefs(row.branches.uncoveredBranches)}`)
|
||||
}
|
||||
lines.push('')
|
||||
}
|
||||
|
||||
if (ignoredDiffLines.length > 0) {
|
||||
lines.push('Ignored changed lines via pragma:')
|
||||
for (const ignoredLine of ignoredDiffLines) {
|
||||
for (const ignoredLine of ignoredDiffLines)
|
||||
lines.push(`- ${ignoredLine.file.replace('web/', '')}:${ignoredLine.line} - ${ignoredLine.reason}`)
|
||||
}
|
||||
lines.push('')
|
||||
}
|
||||
|
||||
if (invalidIgnorePragmas.length > 0) {
|
||||
lines.push('Invalid diff coverage ignore pragmas:')
|
||||
for (const invalidPragma of invalidIgnorePragmas) {
|
||||
for (const invalidPragma of invalidIgnorePragmas)
|
||||
lines.push(`- ${invalidPragma.file.replace('web/', '')}:${invalidPragma.line} - ${invalidPragma.reason}`)
|
||||
}
|
||||
lines.push('')
|
||||
}
|
||||
|
||||
lines.push(`Changed source files checked: ${changedSourceFiles.length}`)
|
||||
lines.push(`Changed statement coverage: ${formatDiffPercent(diffTotals.statements)}`)
|
||||
lines.push(`Changed branch coverage: ${formatDiffPercent(diffTotals.branches)}`)
|
||||
lines.push('Blocking rules: uncovered changed statements, uncovered changed branches, invalid ignore pragmas.')
|
||||
|
||||
return lines
|
||||
}
|
||||
|
||||
function buildSkipSummary(changedExcludedSourceFiles) {
|
||||
const lines = [
|
||||
'### app/components Diff Coverage',
|
||||
'### app/components Pure Diff Coverage',
|
||||
'',
|
||||
`Excluded modules: \`${EXCLUDED_MODULES_LABEL}\``,
|
||||
`Excluded file kinds: \`${COMPONENT_COVERAGE_EXCLUDE_LABEL}\``,
|
||||
@ -357,18 +229,18 @@ function buildSkipSummary(changedExcludedSourceFiles) {
|
||||
]
|
||||
|
||||
if (changedExcludedSourceFiles.length > 0) {
|
||||
lines.push('Only excluded component modules or type-only files changed, so diff coverage check was skipped.')
|
||||
lines.push('Only excluded component modules or type-only files changed, so pure diff coverage was skipped.')
|
||||
lines.push(`Skipped files: ${changedExcludedSourceFiles.length}`)
|
||||
}
|
||||
else {
|
||||
lines.push('No source changes under tracked `web/app/components/`. Diff coverage check skipped.')
|
||||
lines.push('No tracked source changes under `web/app/components/`. Pure diff coverage skipped.')
|
||||
}
|
||||
|
||||
return lines
|
||||
}
|
||||
|
||||
function getChangedFiles(base, head) {
|
||||
const output = execGit(['diff', '--name-only', '--diff-filter=ACMR', ...buildGitDiffRevisionArgs(base, head, DIFF_RANGE_MODE), '--', 'web/app/components', 'web/__tests__'])
|
||||
const output = execGit(['diff', '--name-only', '--diff-filter=ACMR', ...buildGitDiffRevisionArgs(base, head, DIFF_RANGE_MODE), '--', APP_COMPONENTS_PREFIX])
|
||||
return output
|
||||
.split('\n')
|
||||
.map(line => line.trim())
|
||||
@ -376,127 +248,8 @@ function getChangedFiles(base, head) {
|
||||
}
|
||||
|
||||
function getChangedLineMap(base, head) {
|
||||
const diff = execGit(['diff', '--unified=0', '--no-color', '--diff-filter=ACMR', ...buildGitDiffRevisionArgs(base, head, DIFF_RANGE_MODE), '--', 'web/app/components'])
|
||||
return parseChangedLineMap(diff, isTrackedComponentSourceFile)
|
||||
}
|
||||
|
||||
function isAnyComponentSourceFile(filePath) {
|
||||
return filePath.startsWith(APP_COMPONENTS_PREFIX)
|
||||
&& /\.(?:ts|tsx)$/.test(filePath)
|
||||
&& !isTestLikePath(filePath)
|
||||
}
|
||||
|
||||
function isTrackedComponentSourceFile(filePath) {
|
||||
return isAnyComponentSourceFile(filePath)
|
||||
&& !isExcludedComponentSourceFile(filePath)
|
||||
}
|
||||
|
||||
function isExcludedComponentSourceFile(filePath) {
|
||||
return isAnyComponentSourceFile(filePath)
|
||||
&& (
|
||||
EXCLUDED_COMPONENT_MODULES.has(getModuleName(filePath))
|
||||
|| excludedComponentCoverageFiles.has(filePath)
|
||||
)
|
||||
}
|
||||
|
||||
function isRelevantTestFile(filePath) {
|
||||
return filePath.startsWith(SHARED_TEST_PREFIX)
|
||||
|| (filePath.startsWith(APP_COMPONENTS_PREFIX) && isTestLikePath(filePath) && !isExcludedComponentTestFile(filePath))
|
||||
}
|
||||
|
||||
function isExcludedComponentTestFile(filePath) {
|
||||
if (!filePath.startsWith(APP_COMPONENTS_PREFIX))
|
||||
return false
|
||||
|
||||
return EXCLUDED_COMPONENT_MODULES.has(getModuleName(filePath))
|
||||
}
|
||||
|
||||
function isTestLikePath(filePath) {
|
||||
return /(?:^|\/)__tests__\//.test(filePath)
|
||||
|| /(?:^|\/)__mocks__\//.test(filePath)
|
||||
|| /\.(?:spec|test)\.(?:ts|tsx)$/.test(filePath)
|
||||
|| /\.stories\.(?:ts|tsx)$/.test(filePath)
|
||||
|| /\.d\.ts$/.test(filePath)
|
||||
}
|
||||
|
||||
function getCoverageStats(entry) {
|
||||
const lineHits = getLineHits(entry)
|
||||
const statementHits = Object.values(entry.s ?? {})
|
||||
const functionHits = Object.values(entry.f ?? {})
|
||||
const branchHits = Object.values(entry.b ?? {}).flat()
|
||||
|
||||
return {
|
||||
lines: {
|
||||
covered: Object.values(lineHits).filter(count => count > 0).length,
|
||||
total: Object.keys(lineHits).length,
|
||||
},
|
||||
statements: {
|
||||
covered: statementHits.filter(count => count > 0).length,
|
||||
total: statementHits.length,
|
||||
},
|
||||
functions: {
|
||||
covered: functionHits.filter(count => count > 0).length,
|
||||
total: functionHits.length,
|
||||
},
|
||||
branches: {
|
||||
covered: branchHits.filter(count => count > 0).length,
|
||||
total: branchHits.length,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
function sumCoverageStats(rows) {
|
||||
const total = createEmptyCoverageStats()
|
||||
for (const row of rows)
|
||||
addCoverageStats(total, row)
|
||||
return total
|
||||
}
|
||||
|
||||
function mergeCoverageStats(map, moduleName, stats) {
|
||||
const existing = map.get(moduleName) ?? createEmptyCoverageStats()
|
||||
addCoverageStats(existing, stats)
|
||||
map.set(moduleName, existing)
|
||||
}
|
||||
|
||||
function addCoverageStats(target, source) {
|
||||
for (const metric of ['lines', 'statements', 'functions', 'branches']) {
|
||||
target[metric].covered += source[metric].covered
|
||||
target[metric].total += source[metric].total
|
||||
}
|
||||
}
|
||||
|
||||
function createEmptyCoverageStats() {
|
||||
return {
|
||||
lines: { covered: 0, total: 0 },
|
||||
statements: { covered: 0, total: 0 },
|
||||
functions: { covered: 0, total: 0 },
|
||||
branches: { covered: 0, total: 0 },
|
||||
}
|
||||
}
|
||||
|
||||
function getThresholdFailures(stats, thresholds) {
|
||||
const failures = []
|
||||
for (const metric of ['lines', 'statements', 'functions', 'branches']) {
|
||||
const actual = percentage(stats[metric].covered, stats[metric].total)
|
||||
const expected = thresholds[metric]
|
||||
if (actual < expected) {
|
||||
failures.push({
|
||||
metric,
|
||||
actual,
|
||||
expected,
|
||||
})
|
||||
}
|
||||
}
|
||||
return failures
|
||||
}
|
||||
|
||||
function getModuleName(filePath) {
|
||||
const relativePath = filePath.slice(APP_COMPONENTS_PREFIX.length)
|
||||
if (!relativePath)
|
||||
return '(root)'
|
||||
|
||||
const segments = relativePath.split('/')
|
||||
return segments.length === 1 ? '(root)' : segments[0]
|
||||
const diff = execGit(['diff', '--unified=0', '--no-color', '--diff-filter=ACMR', ...buildGitDiffRevisionArgs(base, head, DIFF_RANGE_MODE), '--', APP_COMPONENTS_PREFIX])
|
||||
return parseChangedLineMap(diff, filePath => isTrackedComponentSourceFile(filePath, context.excludedComponentCoverageFiles))
|
||||
}
|
||||
|
||||
function formatLineRanges(lines) {
|
||||
@ -536,10 +289,6 @@ function percentage(covered, total) {
|
||||
return (covered / total) * 100
|
||||
}
|
||||
|
||||
function formatPercent(metric) {
|
||||
return `${percentage(metric.covered, metric.total).toFixed(2)}%`
|
||||
}
|
||||
|
||||
function formatDiffPercent(metric) {
|
||||
if (metric.total === 0)
|
||||
return 'n/a'
|
||||
|
||||
Reference in New Issue
Block a user