@code-pushup/coverage-plugin 0.44.2 → 0.45.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +6 -1
- package/bin.js +150 -5
- package/index.js +1 -1
- package/package.json +3 -3
- package/src/lib/runner/constants.d.ts +1 -0
- package/src/lib/runner/lcov/merge-lcov.d.ts +6 -0
package/README.md
CHANGED
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
🧪 **Code PushUp plugin for tracking code coverage.** ☂️
|
|
8
8
|
|
|
9
9
|
This plugin allows you to measure and track code coverage on your project.
|
|
10
|
+
It accepts the LCOV coverage format and merges coverage results from any test suites provided.
|
|
10
11
|
|
|
11
12
|
Measured coverage types are mapped to Code PushUp audits in the following way
|
|
12
13
|
|
|
@@ -213,7 +214,11 @@ For instance, the following can be an audit output for line coverage.
|
|
|
213
214
|
}
|
|
214
215
|
```
|
|
215
216
|
|
|
216
|
-
###
|
|
217
|
+
### Coverage results alteration
|
|
218
|
+
|
|
219
|
+
At the moment, the LCOV results include `(empty-report)` functions with missing coverage. These point to various imports or exports, not actual functions. For that reason, they are omitted from the results.
|
|
220
|
+
|
|
221
|
+
## Providing coverage results in Nx monorepo
|
|
217
222
|
|
|
218
223
|
As a part of the plugin, there is a `getNxCoveragePaths` helper for setting up paths to coverage results if you are using Nx. The helper accepts all relevant targets (e.g. `test` or `unit-test`) and searches for a coverage path option.
|
|
219
224
|
Jest and Vitest configuration options are currently supported:
|
package/bin.js
CHANGED
|
@@ -1156,10 +1156,134 @@ var PLUGIN_CONFIG_PATH = join2(
|
|
|
1156
1156
|
WORKDIR,
|
|
1157
1157
|
"plugin-config.json"
|
|
1158
1158
|
);
|
|
1159
|
+
var INVALID_FUNCTION_NAME = "(empty-report)";
|
|
1159
1160
|
|
|
1160
1161
|
// packages/plugin-coverage/src/lib/runner/lcov/lcov-runner.ts
|
|
1161
1162
|
import { join as join3 } from "node:path";
|
|
1162
1163
|
|
|
1164
|
+
// packages/plugin-coverage/src/lib/runner/lcov/merge-lcov.ts
|
|
1165
|
+
function mergeLcovResults(records) {
|
|
1166
|
+
const allFilenames = records.map((record) => record.file);
|
|
1167
|
+
if (allFilenames.length === new Set(allFilenames).size) {
|
|
1168
|
+
return records;
|
|
1169
|
+
}
|
|
1170
|
+
return records.reduce((accMerged, currRecord, currIndex) => {
|
|
1171
|
+
const filePath = currRecord.file;
|
|
1172
|
+
const lines6 = currRecord.lines.found;
|
|
1173
|
+
const duplicates = records.reduce(
|
|
1174
|
+
(acc, candidateRecord, candidateIndex) => {
|
|
1175
|
+
if (candidateRecord.file === filePath && candidateRecord.lines.found === lines6 && candidateIndex !== currIndex) {
|
|
1176
|
+
return [...acc, [candidateRecord, candidateIndex]];
|
|
1177
|
+
}
|
|
1178
|
+
return acc;
|
|
1179
|
+
},
|
|
1180
|
+
[]
|
|
1181
|
+
);
|
|
1182
|
+
if (duplicates.map((duplicate) => duplicate[1]).some((index) => index < currIndex)) {
|
|
1183
|
+
return accMerged;
|
|
1184
|
+
}
|
|
1185
|
+
if (duplicates.length === 0) {
|
|
1186
|
+
return [...accMerged, currRecord];
|
|
1187
|
+
}
|
|
1188
|
+
return [
|
|
1189
|
+
...accMerged,
|
|
1190
|
+
mergeDuplicateLcovRecords([
|
|
1191
|
+
currRecord,
|
|
1192
|
+
...duplicates.map((duplicate) => duplicate[0])
|
|
1193
|
+
])
|
|
1194
|
+
];
|
|
1195
|
+
}, []);
|
|
1196
|
+
}
|
|
1197
|
+
function mergeDuplicateLcovRecords(records) {
|
|
1198
|
+
const linesDetails = mergeLcovLineDetails(
|
|
1199
|
+
records.map((record) => record.lines.details)
|
|
1200
|
+
);
|
|
1201
|
+
const linesHit = linesDetails.reduce(
|
|
1202
|
+
(acc, line) => acc + (line.hit > 0 ? 1 : 0),
|
|
1203
|
+
0
|
|
1204
|
+
);
|
|
1205
|
+
const branchesDetails = mergeLcovBranchesDetails(
|
|
1206
|
+
records.map((record) => record.branches.details)
|
|
1207
|
+
);
|
|
1208
|
+
const branchesHit = branchesDetails.reduce(
|
|
1209
|
+
(acc, branch) => acc + (branch.taken > 0 ? 1 : 0),
|
|
1210
|
+
0
|
|
1211
|
+
);
|
|
1212
|
+
const functionsDetails = mergeLcovFunctionsDetails(
|
|
1213
|
+
records.map((record) => record.functions.details)
|
|
1214
|
+
);
|
|
1215
|
+
const functionsHit = functionsDetails.reduce(
|
|
1216
|
+
(acc, func) => acc + (func.hit != null && func.hit > 0 ? 1 : 0),
|
|
1217
|
+
0
|
|
1218
|
+
);
|
|
1219
|
+
const mergedRecord = {
|
|
1220
|
+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
1221
|
+
file: records[0].file,
|
|
1222
|
+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
1223
|
+
title: records[0].title,
|
|
1224
|
+
lines: {
|
|
1225
|
+
found: linesDetails.length,
|
|
1226
|
+
hit: linesHit,
|
|
1227
|
+
details: linesDetails
|
|
1228
|
+
},
|
|
1229
|
+
branches: {
|
|
1230
|
+
found: branchesDetails.length,
|
|
1231
|
+
hit: branchesHit,
|
|
1232
|
+
details: branchesDetails
|
|
1233
|
+
},
|
|
1234
|
+
functions: {
|
|
1235
|
+
found: functionsDetails.length,
|
|
1236
|
+
hit: functionsHit,
|
|
1237
|
+
details: functionsDetails
|
|
1238
|
+
}
|
|
1239
|
+
};
|
|
1240
|
+
return mergedRecord;
|
|
1241
|
+
}
|
|
1242
|
+
function mergeLcovLineDetails(details4) {
|
|
1243
|
+
const flatDetails = details4.flat();
|
|
1244
|
+
const uniqueLines = [
|
|
1245
|
+
...new Set(flatDetails.map((flatDetail) => flatDetail.line))
|
|
1246
|
+
];
|
|
1247
|
+
return uniqueLines.map((line) => {
|
|
1248
|
+
const hitSum = flatDetails.filter((lineDetail) => lineDetail.line === line).reduce((acc, lineDetail) => acc + lineDetail.hit, 0);
|
|
1249
|
+
return { line, hit: hitSum };
|
|
1250
|
+
});
|
|
1251
|
+
}
|
|
1252
|
+
function mergeLcovBranchesDetails(details4) {
|
|
1253
|
+
const flatDetails = details4.flat();
|
|
1254
|
+
const uniqueBranches = [
|
|
1255
|
+
...new Set(
|
|
1256
|
+
flatDetails.map(
|
|
1257
|
+
({ line, block, branch }) => JSON.stringify({ line, block, branch })
|
|
1258
|
+
)
|
|
1259
|
+
)
|
|
1260
|
+
].map(
|
|
1261
|
+
(functionJSON) => JSON.parse(functionJSON)
|
|
1262
|
+
);
|
|
1263
|
+
return uniqueBranches.map(({ line, block, branch }) => {
|
|
1264
|
+
const takenSum = flatDetails.filter(
|
|
1265
|
+
(branchDetail) => branchDetail.line === line && branchDetail.block === block && branchDetail.branch === branch
|
|
1266
|
+
).reduce((acc, branchDetail) => acc + branchDetail.taken, 0);
|
|
1267
|
+
return { line, block, branch, taken: takenSum };
|
|
1268
|
+
});
|
|
1269
|
+
}
|
|
1270
|
+
function mergeLcovFunctionsDetails(details4) {
|
|
1271
|
+
const flatDetails = details4.flat();
|
|
1272
|
+
const uniqueFunctions = [
|
|
1273
|
+
...new Set(
|
|
1274
|
+
flatDetails.map(({ line, name }) => JSON.stringify({ line, name }))
|
|
1275
|
+
)
|
|
1276
|
+
].map(
|
|
1277
|
+
(functionJSON) => JSON.parse(functionJSON)
|
|
1278
|
+
);
|
|
1279
|
+
return uniqueFunctions.map(({ line, name }) => {
|
|
1280
|
+
const hitSum = flatDetails.filter(
|
|
1281
|
+
(functionDetail) => functionDetail.line === line && functionDetail.name === name
|
|
1282
|
+
).reduce((acc, functionDetail) => acc + (functionDetail.hit ?? 0), 0);
|
|
1283
|
+
return { line, name, hit: hitSum };
|
|
1284
|
+
});
|
|
1285
|
+
}
|
|
1286
|
+
|
|
1163
1287
|
// packages/plugin-coverage/src/lib/runner/lcov/parse-lcov.ts
|
|
1164
1288
|
import parseLcovExport from "parse-lcov";
|
|
1165
1289
|
var godKnows = parseLcovExport;
|
|
@@ -1184,21 +1308,41 @@ function mergeConsecutiveNumbers(numberArr) {
|
|
|
1184
1308
|
|
|
1185
1309
|
// packages/plugin-coverage/src/lib/runner/lcov/transform.ts
|
|
1186
1310
|
function lcovReportToFunctionStat(record) {
|
|
1311
|
+
const validRecord = removeEmptyReport(record);
|
|
1187
1312
|
return {
|
|
1188
|
-
totalFound:
|
|
1189
|
-
totalHit:
|
|
1190
|
-
issues:
|
|
1313
|
+
totalFound: validRecord.functions.found,
|
|
1314
|
+
totalHit: validRecord.functions.hit,
|
|
1315
|
+
issues: validRecord.functions.hit < validRecord.functions.found ? validRecord.functions.details.filter((detail) => !detail.hit).map(
|
|
1191
1316
|
(detail) => ({
|
|
1192
1317
|
message: `Function ${detail.name} is not called in any test case.`,
|
|
1193
1318
|
severity: "error",
|
|
1194
1319
|
source: {
|
|
1195
|
-
file:
|
|
1320
|
+
file: validRecord.file,
|
|
1196
1321
|
position: { startLine: detail.line }
|
|
1197
1322
|
}
|
|
1198
1323
|
})
|
|
1199
1324
|
) : []
|
|
1200
1325
|
};
|
|
1201
1326
|
}
|
|
1327
|
+
function removeEmptyReport(record) {
|
|
1328
|
+
const validFunctions = record.functions.details.filter(
|
|
1329
|
+
(detail) => detail.name !== INVALID_FUNCTION_NAME
|
|
1330
|
+
);
|
|
1331
|
+
if (validFunctions.length === record.functions.found) {
|
|
1332
|
+
return record;
|
|
1333
|
+
}
|
|
1334
|
+
return {
|
|
1335
|
+
...record,
|
|
1336
|
+
functions: {
|
|
1337
|
+
details: validFunctions,
|
|
1338
|
+
found: validFunctions.length,
|
|
1339
|
+
hit: validFunctions.reduce(
|
|
1340
|
+
(acc, fn) => acc + (fn.hit != null && fn.hit > 0 ? 1 : 0),
|
|
1341
|
+
0
|
|
1342
|
+
)
|
|
1343
|
+
}
|
|
1344
|
+
};
|
|
1345
|
+
}
|
|
1202
1346
|
function lcovReportToLineStat(record) {
|
|
1203
1347
|
const missingCoverage = record.lines.hit < record.lines.found;
|
|
1204
1348
|
const lines6 = missingCoverage ? record.lines.details.filter((detail) => !detail.hit).map((detail) => detail.line) : [];
|
|
@@ -1263,8 +1407,9 @@ function lcovCoverageToAuditOutput(stat2, coverageType) {
|
|
|
1263
1407
|
// packages/plugin-coverage/src/lib/runner/lcov/lcov-runner.ts
|
|
1264
1408
|
async function lcovResultsToAuditOutputs(results, coverageTypes) {
|
|
1265
1409
|
const lcovResults = await parseLcovFiles(results);
|
|
1410
|
+
const mergedResults = mergeLcovResults(lcovResults);
|
|
1266
1411
|
const totalCoverageStats = getTotalCoverageFromLcovRecords(
|
|
1267
|
-
|
|
1412
|
+
mergedResults,
|
|
1268
1413
|
coverageTypes
|
|
1269
1414
|
);
|
|
1270
1415
|
return coverageTypes.map((coverageType) => {
|
package/index.js
CHANGED
|
@@ -1081,7 +1081,7 @@ import chalk4 from "chalk";
|
|
|
1081
1081
|
|
|
1082
1082
|
// packages/plugin-coverage/package.json
|
|
1083
1083
|
var name = "@code-pushup/coverage-plugin";
|
|
1084
|
-
var version = "0.
|
|
1084
|
+
var version = "0.45.0";
|
|
1085
1085
|
|
|
1086
1086
|
// packages/plugin-coverage/src/lib/config.ts
|
|
1087
1087
|
import { z as z16 } from "zod";
|
package/package.json
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@code-pushup/coverage-plugin",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.45.0",
|
|
4
4
|
"dependencies": {
|
|
5
|
-
"@code-pushup/models": "0.
|
|
6
|
-
"@code-pushup/utils": "0.
|
|
5
|
+
"@code-pushup/models": "0.45.0",
|
|
6
|
+
"@code-pushup/utils": "0.45.0",
|
|
7
7
|
"parse-lcov": "^1.0.4",
|
|
8
8
|
"chalk": "^5.3.0",
|
|
9
9
|
"zod": "^3.22.4"
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { BranchesDetails, FunctionsDetails, LCOVRecord, LinesDetails } from 'parse-lcov';
|
|
2
|
+
export declare function mergeLcovResults(records: LCOVRecord[]): LCOVRecord[];
|
|
3
|
+
export declare function mergeDuplicateLcovRecords(records: LCOVRecord[]): LCOVRecord;
|
|
4
|
+
export declare function mergeLcovLineDetails(details: LinesDetails[][]): LinesDetails[];
|
|
5
|
+
export declare function mergeLcovBranchesDetails(details: BranchesDetails[][]): BranchesDetails[];
|
|
6
|
+
export declare function mergeLcovFunctionsDetails(details: FunctionsDetails[][]): FunctionsDetails[];
|