monocart-coverage-reports
Version:
A code coverage tool to generate native V8 reports or Istanbul reports.
349 lines (297 loc) • 8.36 kB
JavaScript
const CG = require('console-grid');
const EC = require('eight-colors');
const Util = require('./util.js');
const mergeSingleSubGroups = (item) => {
if (!item.subs) {
return;
}
if (item.subs.length === 1) {
const sub = item.subs[0];
if (!sub.subs) {
return;
}
item.name = [item.name, sub.name].filter((it) => it).join('/');
item.subs = sub.subs;
mergeSingleSubGroups(item);
return;
}
item.subs.forEach((sub) => {
mergeSingleSubGroups(sub);
});
};
const getGroupedRows = (flatRows) => {
let groups = [];
flatRows.forEach((file) => {
const pathList = file.name.split('/');
const lastName = pathList.pop();
let subs = groups;
pathList.forEach((key) => {
const item = subs.find((it) => it.name === key && it.subs);
if (item) {
subs = item.subs;
return;
}
const sub = {
name: key,
subs: []
};
subs.push(sub);
subs = sub.subs;
});
let filename = lastName;
if (file.nameStatus) {
filename = Util.getColorStrByStatus(lastName, file.nameStatus, 'ansicode');
}
file.name = filename;
subs.push(file);
});
const group = {
subs: groups
};
mergeSingleSubGroups(group);
if (group.name) {
groups = [group];
}
return groups;
};
// ============================================================
const diffUncoveredLines = (list) => {
const [oldStr, newStr] = list;
const oldList = oldStr.split(',');
const newList = newStr.split(',');
const oldColor = oldList.map((it) => {
if (newList.includes(it)) {
return it;
}
return EC.green(it);
}).join(',');
const newColor = newList.map((it) => {
if (oldList.includes(it)) {
return it;
}
return EC.red(it);
}).join(',');
return [oldColor, newColor];
};
const getDiffMessage = (results, columns, diffOptions) => {
const flatRows = [];
results.forEach((it) => {
if (!it.change && diffOptions.skipEqual) {
return;
}
flatRows.push(it);
});
const rows = getGroupedRows(flatRows);
const addedList = [];
Util.forEach(rows, (it, parent) => {
if (!it.change) {
return;
}
// add color
if (it.change === '-' || it.change === '+') {
columns.forEach((c) => {
const k = c.id;
let v = it[k];
if (k === 'name') {
v = `${it.change} ${v}`;
}
it[k] = EC.red(v);
});
return;
}
// diff
const cloneRow = {};
columns.forEach((c) => {
const k = c.id;
let v = it[k];
if (Array.isArray(v)) {
if (k === 'uncoveredLines') {
v = diffUncoveredLines(v);
} else {
v = [EC.green(v[0]), EC.red(v[1])];
}
it[k] = v[0];
cloneRow[k] = v[1];
return;
}
it[k] = v;
cloneRow[k] = '';
});
addedList.push({
subs: parent ? parent.subs : rows,
row: it,
cloneRow
});
});
addedList.forEach((it) => {
const {
subs, row, cloneRow
} = it;
const i = subs.findIndex((r) => r === row);
subs.splice(i + 1, 0, cloneRow);
});
// add innerBorder for summary
const summaryIndex = rows.findIndex((it) => it.isSummary);
if (summaryIndex !== -1) {
rows.splice(summaryIndex, 0, {
innerBorder: true
});
}
const lines = CG({
options: {
silent: true,
nullPlaceholder: '',
defaultMaxWidth: diffOptions.maxCols
},
columns,
rows
});
return lines.join('\n');
};
// ============================================================
const getSnapshot = (reportData) => {
const {
summary, files, type
} = reportData;
const snapshot = {
type,
summary: {},
files: {}
};
const addPercent = (target, fromSummary) => {
Object.keys(fromSummary).forEach((k) => {
let percent = fromSummary[k].pct;
if (typeof percent === 'number') {
percent = Util.PSF(percent, 100, 2);
}
target[k] = percent;
});
};
addPercent(snapshot.summary, summary);
files.sort((a, b) => {
if (a.sourcePath > b.sourcePath) {
return 1;
}
return -1;
});
files.forEach((file) => {
// do NOT add debug file
if (file.debug) {
return;
}
const fileSummary = {};
addPercent(fileSummary, file.summary);
fileSummary.uncoveredLines = Util.getUncoveredLines(file.data.lines);
// no extras for istanbul
const extras = file.data.extras;
fileSummary.extras = Object.keys(extras).map((k) => {
return k + extras[k];
}).join(',');
snapshot.files[file.sourcePath] = fileSummary;
});
return snapshot;
};
const diffSnapshot = (oldData, newData, diffOptions) => {
diffOptions = {
skipEqual: true,
showSummary: true,
maxCols: 50,
metrics: [],
... diffOptions
};
let change = false;
const results = [];
const metrics = Util.getMetrics(diffOptions.metrics, oldData.type);
const columns = [{
id: 'name',
name: 'Name'
}, ... metrics.map((m) => {
return {
id: m,
name: Util.capitalizeFirstLetter(m),
align: 'right'
};
}), {
id: 'uncoveredLines',
name: 'Uncovered Lines'
}];
const keys = columns.map((it) => it.id).filter((it) => it !== 'name');
const oFiles = oldData.files;
const nFiles = newData.files;
Object.keys(oFiles).forEach((oPath) => {
const oSummary = oFiles[oPath];
const diffResult = {
name: oPath
};
keys.forEach((k) => {
diffResult[k] = oSummary[k];
});
const nSummary = nFiles[oPath];
if (nSummary) {
let fileChange = false;
keys.forEach((k) => {
const oValue = oSummary[k];
const nValue = nSummary[k];
if (oValue !== nValue) {
change = true;
fileChange = true;
diffResult[k] = [oValue, nValue];
}
});
diffResult.change = fileChange;
} else {
change = true;
diffResult.change = '-';
}
results.push(diffResult);
});
// added
Object.keys(nFiles).forEach((nPath) => {
const oSummary = oFiles[nPath];
if (oSummary) {
return;
}
const nSummary = nFiles[nPath];
const diffResult = {
name: nPath,
change: '+'
};
change = true;
keys.forEach((k) => {
diffResult[k] = nSummary[k];
});
results.push(diffResult);
});
if (diffOptions.showSummary) {
const oSummary = oldData.summary;
const nSummary = newData.summary;
const diffResult = {
name: 'Summary',
isSummary: true,
change: false
};
keys.forEach((k) => {
const oValue = oSummary[k];
const nValue = nSummary[k];
if (oValue === nValue) {
diffResult[k] = oValue;
} else {
change = true;
diffResult.change = true;
diffResult[k] = [oValue, nValue];
}
});
results.push(diffResult);
}
const message = getDiffMessage(results, columns, diffOptions);
return {
change,
results,
message
};
};
module.exports = {
getGroupedRows,
getSnapshot,
diffSnapshot
};