Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

monocart-coverage-reports

Package Overview
Dependencies
Maintainers
1
Versions
91
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

monocart-coverage-reports - npm Package Compare versions

Comparing version 2.8.7 to 2.9.0

lib/utils/markdown.js

16

lib/converter/converter.js

@@ -118,8 +118,13 @@ /**

let commentCount = 0;
const dataExtras = {};
// line 1 based
const lineMap = new Map();
locator.lines.forEach((lineItem) => {
// line 1-base
const line = lineItem.line + 1;
// exclude blank and comment
if (lineItem.blank) {
blankCount += 1;
dataExtras[line] = 'b';
return;

@@ -129,2 +134,3 @@ }

commentCount += 1;
dataExtras[line] = 'c';
return;

@@ -134,6 +140,6 @@ }

if (ignored) {
dataExtras[line] = 'i';
return;
}
// line 1-base
const line = lineItem.line + 1;
lineItem.coveredCount = 1;

@@ -170,2 +176,3 @@ lineItem.uncoveredEntire = null;

dataLines,
dataExtras,
summaryLines

@@ -269,5 +276,8 @@ };

// after bytes with ignored, before calculateV8Lines
const { dataLines, summaryLines } = handleLinesCoverage(data.bytes, locator, ignoredRanges);
const {
dataLines, dataExtras, summaryLines
} = handleLinesCoverage(data.bytes, locator, ignoredRanges);
data.lines = dataLines;
data.extras = dataExtras;

@@ -274,0 +284,0 @@ // console.log('statements', state.sourcePath, statements.length);

@@ -30,2 +30,5 @@ module.exports = {

// {string} coverage data dir, alternative to method `addFromDir()`, defaults to null
dataDir: null,
// (V8 only) {function} A filter function to execute for each element in the V8 list.

@@ -32,0 +35,0 @@ // entryFilter: (entry) => {

266

lib/generate.js

@@ -19,2 +19,3 @@ const fs = require('fs');

const { getGroupedRows } = require('./utils/snapshot.js');
const generateMarkdownGrid = require('./utils/markdown.js');

@@ -97,2 +98,4 @@ // ========================================================================================================

'console-summary': 'both',
'markdown-details': 'both',
'markdown-summary': 'both',
'raw': 'both'

@@ -134,9 +137,2 @@ };

const pFormatter = (v, row, column) => {
if (typeof v === 'number') {
return Util.getColorStrByStatus(Util.PSF(v, 100, 2), row.status);
}
return v;
};
const nFormatter = (v) => {

@@ -149,3 +145,3 @@ if (typeof v === 'number') {

const getRowData = (rowName, summary, metrics) => {
const getRowData = (rowName, summary, metrics, color) => {
const summaryRow = {};

@@ -165,3 +161,3 @@ let lowest = {

}
summaryRow[k] = Util.getColorStrByStatus(Util.PSF(percent, 100, 2), s.status);
summaryRow[k] = Util.getColorStrByStatus(Util.PSF(percent, 100, 2), s.status, color);
if (percent < lowest.pct) {

@@ -176,62 +172,4 @@ lowest = s;

const getUncoveredLines = (file) => {
const lines = [];
const getDetailsRows = (files, metrics, cdOptions, color) => {
const dataLines = file.data.lines;
let startLine;
let endLine;
const addLines = () => {
if (!startLine) {
return;
}
if (endLine) {
// range
const link = startLine.color === 'yellow' && endLine.color === 'yellow' ? EC.yellow('-') : EC.red('-');
lines.push(EC[startLine.color](startLine.line) + link + EC[endLine.color](endLine.line));
startLine = null;
endLine = null;
} else {
// only start
lines.push(EC[startLine.color](startLine.line));
startLine = null;
}
};
const setLines = (line, color) => {
if (startLine) {
endLine = {
line,
color
};
return;
}
startLine = {
line,
color
};
};
Object.keys(dataLines).forEach((line) => {
const count = dataLines[line];
if (count === 0) {
setLines(line, 'red');
return;
}
// 0 < count < 1
if (typeof count === 'string') {
setLines(line, 'yellow');
return;
}
// count >= 1
addLines();
});
addLines();
return lines.join(',');
};
const getDetailsRows = (files, metrics, cdOptions) => {
const skipPercent = cdOptions.skipPercent;

@@ -260,4 +198,4 @@ if (typeof skipPercent === 'number' && skipPercent > 0) {

const { sourcePath, summary } = file;
const fileRow = getRowData(sourcePath, summary, metrics);
fileRow.uncoveredLines = getUncoveredLines(file);
const fileRow = getRowData(sourcePath, summary, metrics, color);
fileRow.uncoveredLines = Util.getUncoveredLines(file.data.lines, color);
flatRows.push(fileRow);

@@ -343,7 +281,9 @@ });

const color = 'ansicode';
const metrics = Util.getMetrics(cdOptions.metrics, type);
const rows = getDetailsRows(files, metrics, cdOptions);
const summaryRow = getRowData('Summary', summary, metrics);
const rows = getDetailsRows(files, metrics, cdOptions, color);
const summaryRow = getRowData('Summary', summary, metrics, color);
if (summaryRow.nameStatus) {
summaryRow.name = Util.getColorStrByStatus(summaryRow.name, summaryRow.nameStatus);
summaryRow.name = Util.getColorStrByStatus(summaryRow.name, summaryRow.nameStatus, color);
}

@@ -383,2 +323,37 @@ // no rows if skipped all by skipPercent

const getSummaryColumns = (color) => {
const columns = [{
id: 'name',
name: 'Name'
}, {
id: 'pct',
name: 'Coverage %',
align: 'right',
formatter: (v, row, column) => {
if (typeof v === 'number') {
return Util.getColorStrByStatus(Util.PSF(v, 100, 2), row.status, color);
}
return v;
}
}, {
id: 'covered',
name: 'Covered',
align: 'right',
formatter: nFormatter
}, {
id: 'uncovered',
name: 'Uncovered',
align: 'right',
formatter: nFormatter
}, {
id: 'total',
name: 'Total',
align: 'right',
formatter: nFormatter
}];
return columns;
};
const handleConsoleSummaryReport = (reportData, reportOptions, options) => {

@@ -408,33 +383,134 @@

const columns = getSummaryColumns('ansicode');
CG({
columns,
rows
});
};
const handleMarkdownDetailsReport = async (reportData, reportOptions, options) => {
const mdOptions = {
baseUrl: '',
// unicode, html, or empty for no color
color: 'unicode',
skipPercent: 0,
metrics: [],
outputFile: 'coverage-details.md',
... reportOptions
};
const color = Util.normalizeColorType(mdOptions.color);
const baseUrl = mdOptions.baseUrl;
const mdPath = path.resolve(options.outputDir, mdOptions.outputFile);
const metrics = Util.getMetrics(mdOptions.metrics, reportData.type);
let files = reportData.files;
const skipPercent = mdOptions.skipPercent;
if (typeof skipPercent === 'number' && skipPercent > 0) {
files = files.filter((file) => {
const { summary } = file;
for (const k of metrics) {
const percent = summary[k].pct;
if (typeof percent === 'number' && percent < skipPercent) {
return true;
}
}
return false;
});
}
const rows = [];
files.forEach((file) => {
// do NOT add debug file
if (file.debug) {
return;
}
const { sourcePath, summary } = file;
const fileRow = getRowData(sourcePath, summary, metrics, color);
if (fileRow.nameStatus) {
const nameColor = baseUrl && color === 'tex' ? '' : color;
fileRow.name = Util.getColorStrByStatus(fileRow.name, fileRow.nameStatus, nameColor);
}
if (baseUrl) {
fileRow.name = `[${fileRow.name}](${baseUrl + sourcePath})`;
}
fileRow.uncoveredLines = Util.getUncoveredLines(file.data.lines, color);
rows.push(fileRow);
});
const summaryRow = getRowData('Summary', reportData.summary, metrics, color);
if (summaryRow.nameStatus) {
summaryRow.name = Util.getColorStrByStatus(summaryRow.name, summaryRow.nameStatus, color);
}
rows.push(summaryRow);
const columns = [{
id: 'name',
name: 'Name'
}, {
id: 'pct',
name: 'Coverage %',
align: 'right',
formatter: pFormatter
}, {
id: 'covered',
name: 'Covered',
align: 'right',
formatter: nFormatter
}, {
id: 'uncovered',
name: 'Uncovered',
align: 'right',
formatter: nFormatter
}, {
id: 'total',
name: 'Total',
align: 'right',
formatter: nFormatter
}, ... metrics.map((m) => {
return {
id: m,
name: Util.capitalizeFirstLetter(m),
align: 'right'
};
}), {
id: 'uncoveredLines',
name: 'Uncovered Lines'
}];
CG({
const markdownGrid = generateMarkdownGrid({
options: {
name: reportData.name
},
columns,
rows
});
await Util.writeFile(mdPath, markdownGrid);
return Util.relativePath(mdPath);
};
const handleMarkdownSummaryReport = async (reportData, reportOptions, options) => {
const msOptions = {
// unicode, html, or empty for no color
color: 'unicode',
metrics: [],
outputFile: 'coverage-summary.md',
... reportOptions
};
const color = Util.normalizeColorType(msOptions.color);
const mdPath = path.resolve(options.outputDir, msOptions.outputFile);
const metrics = Util.getMetrics(msOptions.metrics, reportData.type);
const rows = metrics.map((k) => {
return {
... reportData.summary[k],
name: Util.capitalizeFirstLetter(k)
};
});
const columns = getSummaryColumns(color);
const markdownGrid = generateMarkdownGrid({
options: {
name: reportData.name
},
columns,
rows
});
await Util.writeFile(mdPath, markdownGrid);
return Util.relativePath(mdPath);
};
const handleRawReport = (reportData, reportOptions, options) => {

@@ -517,2 +593,4 @@ const rawOptions = {

'console-summary': handleConsoleSummaryReport,
'markdown-details': handleMarkdownDetailsReport,
'markdown-summary': handleMarkdownSummaryReport,
'raw': handleRawReport

@@ -519,0 +597,0 @@ };

@@ -47,2 +47,5 @@ declare namespace MCR {

['v8'] | ["v8", {
/**
* defaults to `index.html`
*/
outputFile?: string;

@@ -54,2 +57,5 @@ inline?: boolean;

['v8-json'] | ["v8-json", {
/**
* defaults to `coverage-report.json`
*/
outputFile?: string;

@@ -110,5 +116,11 @@ }] |

['codecov'] | ["codecov", {
/**
* defaults to `codecov.json`
*/
outputFile?: string;
}] |
['codacy'] | ["codacy", {
/**
* defaults to `codacy.json`
*/
outputFile?: string;

@@ -124,2 +136,20 @@ }] |

}] |
['markdown-summary'] | ['markdown-summary', {
color: 'unicode' | 'html' | 'tex' | string;
metrics?: Array<"bytes" | "statements" | "branches" | "functions" | "lines">;
/**
* defaults to `coverage-summary.md`
*/
outputFile?: string;
}] |
['markdown-details'] | ['markdown-details', {
baseUrl?: string;
color: 'unicode' | 'html' | 'tex' | string;
skipPercent?: number;
metrics?: Array<"bytes" | "statements" | "branches" | "functions" | "lines">;
/**
* defaults to `coverage-details.md`
*/
outputFile?: string;
}] |
['raw'] | ['raw', {

@@ -200,6 +230,19 @@ outputDir?: string;

ignores?: IgnoredRange[];
/** common json format */
lines: {
lines?: {
/**
* key: line number;
* number: hits (0 means uncovered);
* string: partial covered
*/
[key: string]: number | string;
};
extras?: {
/**
* key: line number;
* b: blank;
* c: comment;
* i: ignored;
*/
[key: string]: "b" | "c" | "i";
}
}

@@ -245,2 +288,5 @@ }

/** {string} coverage data dir, alternative to method `addFromDir()`, defaults to null */
dataDir?: string;
/** (V8 only)

@@ -425,2 +471,3 @@ *

uncoveredLines: string;
extras: string;
}

@@ -427,0 +474,0 @@ }

@@ -139,2 +139,7 @@ const fs = require('fs');

const dataDir = this.options.dataDir;
if (dataDir) {
await this.addFromDir(dataDir);
}
const inputData = await getInputData(this);

@@ -141,0 +146,0 @@ if (!inputData) {

@@ -35,2 +35,4 @@ const istanbulLibReport = require('istanbul-lib-report');

const lines = fileCoverage.getLineCoverage();
// no extras for istanbul
const extras = {};

@@ -41,3 +43,4 @@ this.files.push({

data: {
lines
lines,
extras
}

@@ -44,0 +47,0 @@ });

@@ -208,9 +208,3 @@ const Util = {

// rangeList should be sorted by startKey, but seems useless here
const listStart = rangeList.filter((it) => startPos >= it[startKey]);
if (!listStart.length) {
return;
}
const list = listStart.filter((it) => endPos <= it[endKey]);
const list = rangeList.filter((it) => startPos >= it[startKey] && endPos <= it[endKey]);
if (!list.length) {

@@ -217,0 +211,0 @@ return;

@@ -6,52 +6,2 @@ const CG = require('console-grid');

const getUncoveredLines = (file) => {
const lines = [];
const dataLines = file.data.lines;
let startLine;
let endLine;
const addLines = () => {
if (!startLine) {
return;
}
if (endLine) {
// range
lines.push(`${startLine.line}-${endLine.line}`);
startLine = null;
endLine = null;
} else {
// only start
lines.push(startLine.line);
startLine = null;
}
};
const setLines = (line) => {
if (startLine) {
endLine = {
line
};
return;
}
startLine = {
line
};
};
Object.keys(dataLines).forEach((line) => {
const count = dataLines[line];
if (count === 0 || typeof count === 'string') {
setLines(line);
return;
}
// count >= 1
addLines();
});
addLines();
return lines.join(',');
};
const mergeSingleSubGroups = (item) => {

@@ -101,3 +51,3 @@

if (file.nameStatus) {
filename = Util.getColorStrByStatus(lastName, file.nameStatus);
filename = Util.getColorStrByStatus(lastName, file.nameStatus, 'ansicode');
}

@@ -253,3 +203,6 @@ file.name = filename;

files.sort((a, b) => {
return a.sourcePath - b.sourcePath;
if (a.sourcePath > b.sourcePath) {
return 1;
}
return -1;
});

@@ -263,3 +216,10 @@ files.forEach((file) => {

addPercent(fileSummary, file.summary);
fileSummary.uncoveredLines = getUncoveredLines(file);
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;

@@ -266,0 +226,0 @@ });

@@ -504,15 +504,174 @@ const fs = require('fs');

getColorStrByStatus: (str, status) => {
if (status === 'low') {
return EC.red(str);
normalizeColorType: (color) => {
return `${color}`.trim().toLowerCase();
},
getColorStrByStatus: (str, status, color = '') => {
const colorHandlers = {
'ansicode': () => {
if (status === 'low') {
return EC.red(str);
}
if (status === 'medium') {
return EC.yellow(str);
}
if (status === 'high') {
return EC.green(str);
}
return str;
},
'unicode': () => {
if (status === 'low') {
return `🔴 ${str}`;
}
if (status === 'medium') {
return `🟡 ${str}`;
}
if (status === 'high') {
return `🟢 ${str}`;
}
return str;
},
'html': () => {
if (status === 'low') {
return `<font color="red">${str}</font>`;
}
if (status === 'medium') {
return `<font color="orange">${str}</font>`;
}
if (status === 'high') {
return `<font color="green">${str}</font>`;
}
return str;
},
'tex': () => {
const texStr = str.replace('%', '\\\\%');
if (status === 'low') {
return `$\\color{red}{\\textsf{${texStr}}}$`;
}
if (status === 'medium') {
return `$\\color{orange}{\\textsf{${texStr}}}$`;
}
if (status === 'high') {
return `$\\color{green}{\\textsf{${texStr}}}$`;
}
return str;
}
};
const handler = colorHandlers[color];
if (handler) {
return handler();
}
if (status === 'medium') {
return EC.yellow(str);
}
if (status === 'high') {
return EC.green(str);
}
// no color
return str;
},
getUncoveredLines: (dataLines, color = '') => {
const lines = [];
let startLine;
let endLine;
const getHtmlColor = (c) => {
return c === 'yellow' ? 'orange' : c;
};
const getAnsiLink = () => {
if (startLine.value !== 'red' && endLine.value !== 'red' && endLine.line - startLine.line === 1) {
return EC.yellow('-');
}
return EC.red('-');
};
const getHtmlLink = () => {
if (startLine.value !== 'red' && endLine.value !== 'red' && endLine.line - startLine.line === 1) {
return '<font color="orange">-</font>';
}
return '<font color="red">-</font>';
};
const addRangeItem = () => {
if (color === 'ansicode') {
lines.push(EC[startLine.value](startLine.line) + getAnsiLink() + EC[endLine.value](endLine.line));
return;
}
if (color === 'html') {
lines.push(`<font color="${getHtmlColor(startLine.value)}">${startLine.line}</font>${getHtmlLink()}<font color="${getHtmlColor(endLine.value)}">${endLine.line}</font>`);
return;
}
// no color
lines.push(`${startLine.line}-${endLine.line}`);
};
const addStartItem = () => {
if (color === 'ansicode') {
lines.push(EC[startLine.value](startLine.line));
return;
}
if (color === 'html') {
lines.push(`<font color="${getHtmlColor(startLine.value)}">${startLine.line}</font>`);
return;
}
// no color
lines.push(startLine.line);
};
const addLines = () => {
if (!startLine) {
return;
}
if (endLine) {
// range
addRangeItem();
startLine = null;
endLine = null;
} else {
// only start
addStartItem();
startLine = null;
}
};
const setLines = (line, value) => {
if (startLine) {
endLine = {
line,
value
};
return;
}
startLine = {
line,
value
};
};
Object.keys(dataLines).forEach((line) => {
const count = dataLines[line];
if (count === 0) {
setLines(line, 'red');
return;
}
// 0 < count < 1
if (typeof count === 'string') {
setLines(line, 'yellow');
return;
}
// count >= 1
addLines();
});
addLines();
return lines.join(',');
},
cmpVersion: (v1, v2) => {

@@ -519,0 +678,0 @@ const [strMajor1, strMinor1, strPatch1] = `${v1}`.split('.');

{
"name": "monocart-coverage-reports",
"version": "2.8.7",
"version": "2.9.0",
"description": "A code coverage tool to generate native V8 reports or Istanbul reports.",

@@ -85,3 +85,3 @@ "main": "./lib/index.js",

"monocart-formatter": "^3.0.0",
"monocart-locator": "^1.0.1",
"monocart-locator": "^1.0.2",
"turbogrid": "^3.2.0"

@@ -91,7 +91,7 @@ },

"commander": "^12.1.0",
"esbuild": "^0.22.0",
"esbuild": "^0.23.0",
"eslint": "~9.6.0",
"eslint-config-plus": "^2.0.2",
"eslint-plugin-html": "^8.1.1",
"eslint-plugin-vue": "^9.26.0",
"eslint-plugin-vue": "^9.27.0",
"find-up": "^7.0.0",

@@ -103,5 +103,5 @@ "minimatch": "^9.0.5",

"supports-color": "^9.4.0",
"tsx": "^4.16.0",
"tsx": "^4.16.2",
"ws": "^8.17.1"
}
}

@@ -142,19 +142,24 @@ # Monocart Coverage Reports

- `codecov`
- coverage data for [Codecov](https://docs.codecov.com/docs/codecov-custom-coverage-format), see [example](https://app.codecov.io/github/cenfun/monocart-coverage-reports)
- `codecov` Save coverage data to a json file with [Codecov](https://docs.codecov.com/docs/codecov-custom-coverage-format) format (defaults to `codecov.json`), see [example](https://app.codecov.io/github/cenfun/monocart-coverage-reports).
- `codacy`
- coverage data for [Codacy](https://api.codacy.com/swagger#tocscoveragereport) API
- `codacy` Save coverage data to a json file with [Codacy API](https://api.codacy.com/swagger#tocscoveragereport) format (defaults to `codacy.json`).
- `console-summary` shows coverage summary in the console
- `console-summary` shows coverage summary in the console.
![](./assets/console-summary.png)
- `console-details` Show file coverage and uncovered lines in the console. Like `text`, but for V8. For Github actions, we can enforce color with env: `FORCE_COLOR: true`.
- `console-details` Show coverage details in the console. Like `text`, but for V8. For Github actions, we can enforce color with env: `FORCE_COLOR: true`.
![](./assets/console-details.png)
- `raw` only keep all original data, which can be used for other reports input with `inputDir`
- see [Merge Coverage Reports](#merge-coverage-reports)
- `markdown-summary` Save coverage summary to a markdown file (defaults to `coverage-summary.md`). For Github actions, we can show the markdown content to [a job summary](https://docs.github.com/en/actions/using-workflows/workflow-commands-for-github-actions#adding-a-job-summary)
```sh
cat path-to/coverage-summary.md >> $GITHUB_STEP_SUMMARY
```
![](./assets/markdown-summary.png)
- `markdown-details` Save coverage details to a markdown file (defaults to `coverage-details.md`).
- `raw` only keep all original data, which can be used for other reports input with `inputDir`. see [Merge Coverage Reports](#merge-coverage-reports)
- Custom Reporter

@@ -161,0 +166,0 @@ ```js

@@ -142,7 +142,5 @@ # Monocart Coverage Reports

- `codecov`
- 专属支持[Codecov](https://docs.codecov.com/docs/codecov-custom-coverage-format)的报告, see [example](https://app.codecov.io/github/cenfun/monocart-coverage-reports)
- `codecov` 保存覆盖率数据到 [Codecov](https://docs.codecov.com/docs/codecov-custom-coverage-format) 专属的json文件 (默认是`codecov.json`), 见[例子](https://app.codecov.io/github/cenfun/monocart-coverage-reports)
- `codacy`
- 专属支持[Codacy](https://api.codacy.com/swagger#tocscoveragereport) API的报告
- `codacy` 保存覆盖率数据到 [Codacy](https://api.codacy.com/swagger#tocscoveragereport) 专属的json文件 (默认是`codacy.json`)

@@ -157,5 +155,12 @@ - `console-summary` 在控制台显示覆盖率概要

- `raw` 只是保存原始覆盖率数据, 用于使用`inputDir`参数来导入多个原始数据进行合并报告
- 参见 [合并覆盖率报告](#merge-coverage-reports)
- `markdown-summary` 保存概要信息到markdown文件 (默认是`coverage-summary.md`)。 如果是Github actions, 可以把markdown的内容添加到[a job summary](https://docs.github.com/en/actions/using-workflows/workflow-commands-for-github-actions#adding-a-job-summary)
```sh
cat path-to/coverage-summary.md >> $GITHUB_STEP_SUMMARY
```
![](./assets/markdown-summary.png)
- `markdown-details` 保存覆盖率详情到markdown文件 (默认是 `coverage-details.md`)
- `raw` 只是保存原始覆盖率数据, 用于使用`inputDir`参数来导入多个原始数据进行合并报告。参见 [合并覆盖率报告](#merge-coverage-reports)
- 自定义报告

@@ -162,0 +167,0 @@ ```js

Sorry, the diff of this file is too big to display

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc