monocart-coverage-reports 2.9.3 → 2.10.1

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.
@@ -26,6 +26,22 @@ const Util = {
26
26
  return s.padStart(l, '0');
27
27
  },
28
28
 
29
+ isNum: function(num) {
30
+ if (typeof num !== 'number' || isNaN(num)) {
31
+ return false;
32
+ }
33
+ const isInvalid = function(n) {
34
+ if (n === Number.MAX_VALUE || n === Number.MIN_VALUE || n === Number.NEGATIVE_INFINITY || n === Number.POSITIVE_INFINITY) {
35
+ return true;
36
+ }
37
+ return false;
38
+ };
39
+ if (isInvalid(num)) {
40
+ return false;
41
+ }
42
+ return true;
43
+ },
44
+
29
45
  toNum: function(num, toInt) {
30
46
  if (typeof (num) !== 'number') {
31
47
  num = parseFloat(num);
@@ -322,6 +338,12 @@ const Util = {
322
338
  return lines;
323
339
  },
324
340
 
341
+ initLineCoverage: (lineItem) => {
342
+ lineItem.coveredCount = 1;
343
+ lineItem.uncoveredEntire = null;
344
+ lineItem.uncoveredPieces = [];
345
+ },
346
+
325
347
  updateLinesCoverage: (lines, count, lineMap) => {
326
348
  lines.forEach((it) => {
327
349
  const lineItem = lineMap.get(it.line);
@@ -0,0 +1,43 @@
1
+ const path = require('path');
2
+ const Util = require('../utils/util.js');
3
+
4
+ const codacyReport = async (reportData, reportOptions, options) => {
5
+ const codacyOptions = {
6
+ outputFile: 'codacy.json',
7
+ ... reportOptions
8
+ };
9
+
10
+ const jsonPath = path.resolve(options.outputDir, codacyOptions.outputFile);
11
+
12
+ // https://api.codacy.com/swagger#tocscoveragereport
13
+ const fileReports = [];
14
+ reportData.files.forEach((item) => {
15
+ const { sourcePath, data } = item;
16
+
17
+ const coverage = {};
18
+ for (const [key, value] of Object.entries(data.lines)) {
19
+ if (typeof value === 'number') {
20
+ coverage[key] = value;
21
+ } else {
22
+ // partial coverage not supported?
23
+ coverage[key] = 0;
24
+ }
25
+ }
26
+
27
+ fileReports.push({
28
+ filename: sourcePath,
29
+ coverage
30
+ });
31
+ });
32
+ const codacyData = {
33
+ fileReports
34
+ };
35
+
36
+ await Util.writeFile(jsonPath, JSON.stringify(codacyData));
37
+ return Util.relativePath(jsonPath);
38
+ };
39
+
40
+
41
+ module.exports = {
42
+ codacyReport
43
+ };
@@ -0,0 +1,28 @@
1
+ const path = require('path');
2
+ const Util = require('../utils/util.js');
3
+
4
+ const codecovReport = async (reportData, reportOptions, options) => {
5
+ const codecovOptions = {
6
+ outputFile: 'codecov.json',
7
+ ... reportOptions
8
+ };
9
+
10
+ const jsonPath = path.resolve(options.outputDir, codecovOptions.outputFile);
11
+
12
+ // https://docs.codecov.com/docs/codecov-custom-coverage-format
13
+ const coverage = {};
14
+ reportData.files.forEach((item) => {
15
+ const { sourcePath, data } = item;
16
+ coverage[sourcePath] = data.lines;
17
+ });
18
+ const codecovData = {
19
+ coverage
20
+ };
21
+
22
+ await Util.writeFile(jsonPath, JSON.stringify(codecovData));
23
+ return Util.relativePath(jsonPath);
24
+ };
25
+
26
+ module.exports = {
27
+ codecovReport
28
+ };
@@ -0,0 +1,174 @@
1
+ const CG = require('console-grid');
2
+ const EC = require('eight-colors');
3
+ const { minimatch } = require('../packages/monocart-coverage-vendor.js');
4
+ const { getGroupedRows } = require('../utils/snapshot.js');
5
+ const Util = require('../utils/util.js');
6
+
7
+ const getRowData = (rowName, summary, metrics, color) => {
8
+ const summaryRow = {};
9
+ let lowest = {
10
+ pct: 100,
11
+ status: 'high'
12
+ };
13
+ metrics.map((k) => {
14
+ const s = summary[k];
15
+ if (!s) {
16
+ return;
17
+ }
18
+ const percent = s.pct;
19
+ if (typeof percent !== 'number') {
20
+ return;
21
+ }
22
+ summaryRow[k] = Util.getColorStrByStatus(Util.PSF(percent, 100, 2), s.status, color);
23
+ if (percent < lowest.pct) {
24
+ lowest = s;
25
+ }
26
+ });
27
+ summaryRow.nameStatus = lowest.status;
28
+ summaryRow.name = rowName;
29
+ return summaryRow;
30
+ };
31
+
32
+ const getDetailsRows = (files, metrics, cdOptions, color) => {
33
+
34
+ const skipPercent = cdOptions.skipPercent;
35
+ if (typeof skipPercent === 'number' && skipPercent > 0) {
36
+ files = files.filter((file) => {
37
+ const { summary } = file;
38
+ for (const k of metrics) {
39
+ const percent = summary[k].pct;
40
+ if (typeof percent === 'number' && percent < skipPercent) {
41
+ return true;
42
+ }
43
+ }
44
+ return false;
45
+ });
46
+ }
47
+
48
+ const flatRows = [];
49
+ files.forEach((file) => {
50
+
51
+ // do NOT add debug file
52
+ if (file.debug) {
53
+ return;
54
+ }
55
+
56
+ const { sourcePath, summary } = file;
57
+ const fileRow = getRowData(sourcePath, summary, metrics, color);
58
+ fileRow.uncoveredLines = Util.getUncoveredLines(file.data.lines, color);
59
+ flatRows.push(fileRow);
60
+ });
61
+
62
+ return getGroupedRows(flatRows);
63
+ };
64
+
65
+ const getFileFilter = (input) => {
66
+ // for function handler
67
+ if (typeof input === 'function') {
68
+ return input;
69
+ }
70
+
71
+ // for single minimatch pattern
72
+ if (input && typeof input === 'string') {
73
+ // string to multiple patterns "{...}"
74
+ // mcr npx mocha --entryFilter {'**/node_modules/**':false,'**/src/*.js':true}
75
+ // mcr npx mocha --entryFilter "{'**/node_modules/**': false, '**/src/*.js': true}"
76
+ const obj = Util.strToObj(input);
77
+ if (obj) {
78
+ input = obj;
79
+ } else {
80
+ return (file) => {
81
+ return minimatch(file.sourcePath, input);
82
+ };
83
+ }
84
+ }
85
+
86
+ // for patterns
87
+ if (input && typeof input === 'object') {
88
+ const patterns = Object.keys(input);
89
+ return (file) => {
90
+ const sourcePath = file.sourcePath;
91
+ for (const pattern of patterns) {
92
+ if (minimatch(sourcePath, pattern)) {
93
+ return input[pattern];
94
+ }
95
+ }
96
+ // false if not matched
97
+ };
98
+ }
99
+
100
+ };
101
+
102
+ const getFilteredFiles = (filter, files) => {
103
+ const fileFilter = getFileFilter(filter);
104
+ if (fileFilter) {
105
+ return files.filter(fileFilter);
106
+ }
107
+ return files;
108
+ };
109
+
110
+ const consoleDetailsReport = (reportData, reportOptions, options) => {
111
+ const cdOptions = {
112
+ maxCols: 50,
113
+ skipPercent: 0,
114
+ metrics: [],
115
+ filter: null,
116
+ ... reportOptions
117
+ };
118
+
119
+ const {
120
+ type, name, summary
121
+ } = reportData;
122
+
123
+ if (name) {
124
+ Util.logInfo(EC.cyan(name));
125
+ }
126
+
127
+ const color = 'ansicode';
128
+ let files = reportData.files;
129
+ files = getFilteredFiles(cdOptions.filter, files);
130
+
131
+ const metrics = Util.getMetrics(cdOptions.metrics, type);
132
+ const rows = getDetailsRows(files, metrics, cdOptions, color);
133
+ const summaryRow = getRowData('Summary', summary, metrics, color);
134
+ if (summaryRow.nameStatus) {
135
+ summaryRow.name = Util.getColorStrByStatus(summaryRow.name, summaryRow.nameStatus, color);
136
+ }
137
+ // no rows if skipped all by skipPercent
138
+ if (rows.length) {
139
+ rows.push({
140
+ innerBorder: true
141
+ });
142
+ }
143
+ rows.push(summaryRow);
144
+
145
+ const columns = [{
146
+ id: 'name',
147
+ name: 'Name'
148
+ }, ... metrics.map((m) => {
149
+ return {
150
+ id: m,
151
+ name: Util.capitalizeFirstLetter(m),
152
+ align: 'right'
153
+ };
154
+ }), {
155
+ id: 'uncoveredLines',
156
+ name: 'Uncovered Lines'
157
+ }];
158
+
159
+ return CG({
160
+ options: {
161
+ silent: cdOptions.silent,
162
+ nullPlaceholder: '',
163
+ defaultMaxWidth: cdOptions.maxCols
164
+ },
165
+ columns,
166
+ rows
167
+ });
168
+ };
169
+
170
+ module.exports = {
171
+ getRowData,
172
+ getFilteredFiles,
173
+ consoleDetailsReport
174
+ };
@@ -0,0 +1,83 @@
1
+ const CG = require('console-grid');
2
+ const EC = require('eight-colors');
3
+ const Util = require('../utils/util.js');
4
+
5
+ const nFormatter = (v) => {
6
+ if (typeof v === 'number') {
7
+ return Util.NF(v);
8
+ }
9
+ return v;
10
+ };
11
+
12
+
13
+ const getSummaryColumns = (color) => {
14
+
15
+ const columns = [{
16
+ id: 'name',
17
+ name: 'Name'
18
+ }, {
19
+ id: 'pct',
20
+ name: 'Coverage %',
21
+ align: 'right',
22
+ formatter: (v, row, column) => {
23
+ if (typeof v === 'number') {
24
+ return Util.getColorStrByStatus(Util.PSF(v, 100, 2), row.status, color);
25
+ }
26
+ return v;
27
+ }
28
+ }, {
29
+ id: 'covered',
30
+ name: 'Covered',
31
+ align: 'right',
32
+ formatter: nFormatter
33
+ }, {
34
+ id: 'uncovered',
35
+ name: 'Uncovered',
36
+ align: 'right',
37
+ formatter: nFormatter
38
+ }, {
39
+ id: 'total',
40
+ name: 'Total',
41
+ align: 'right',
42
+ formatter: nFormatter
43
+ }];
44
+
45
+ return columns;
46
+ };
47
+
48
+ const consoleSummaryReport = (reportData, reportOptions, options) => {
49
+
50
+ const csOptions = {
51
+ metrics: [],
52
+ ... reportOptions
53
+ };
54
+
55
+ const {
56
+ summary, name, type
57
+ } = reportData;
58
+
59
+ if (name) {
60
+ Util.logInfo(EC.cyan(name));
61
+ }
62
+
63
+ const metrics = Util.getMetrics(csOptions.metrics, type);
64
+
65
+ const rows = metrics.map((k) => {
66
+ return {
67
+ ... summary[k],
68
+ name: Util.capitalizeFirstLetter(k)
69
+ };
70
+ });
71
+
72
+ const columns = getSummaryColumns('ansicode');
73
+
74
+ CG({
75
+ columns,
76
+ rows
77
+ });
78
+ };
79
+
80
+ module.exports = {
81
+ getSummaryColumns,
82
+ consoleSummaryReport
83
+ };
@@ -0,0 +1,34 @@
1
+ const { pathToFileURL } = require('url');
2
+ const Util = require('../utils/util.js');
3
+
4
+ const customReport = async (reportName, reportData, reportOptions, globalOptions) => {
5
+ let CustomReporter;
6
+ let err;
7
+ try {
8
+ CustomReporter = await import(reportName);
9
+ } catch (e) {
10
+ err = e;
11
+ try {
12
+ CustomReporter = await import(pathToFileURL(reportName));
13
+ } catch (ee) {
14
+ err = ee;
15
+ }
16
+ }
17
+
18
+ if (!CustomReporter) {
19
+ Util.logError(err.message);
20
+ return;
21
+ }
22
+
23
+ CustomReporter = CustomReporter.default || CustomReporter;
24
+
25
+ const reporter = new CustomReporter(reportOptions, globalOptions);
26
+
27
+ const results = await reporter.generate(reportData);
28
+
29
+ return results;
30
+ };
31
+
32
+ module.exports = {
33
+ customReport
34
+ };
@@ -0,0 +1,120 @@
1
+ const path = require('path');
2
+ const Util = require('../utils/util.js');
3
+
4
+ const { getRowData, getFilteredFiles } = require('./console-details.js');
5
+
6
+ const cutWithMaxCols = (str, maxCols, before) => {
7
+ if (str && maxCols && str.length > maxCols) {
8
+ if (before) {
9
+ return `... ${str.slice(str.length - (maxCols - 4))}`;
10
+ }
11
+ return `${str.slice(0, maxCols - 4)} ...`;
12
+ }
13
+
14
+ return str;
15
+ };
16
+
17
+ const markdownDetailsReport = async (reportData, reportOptions, options) => {
18
+ const mdOptions = {
19
+ baseUrl: '',
20
+ // unicode, html, or empty for no color
21
+ color: 'unicode',
22
+ maxCols: 50,
23
+ skipPercent: 0,
24
+ metrics: [],
25
+ filter: null,
26
+ outputFile: 'coverage-details.md',
27
+ ... reportOptions
28
+ };
29
+
30
+ const baseUrl = mdOptions.baseUrl;
31
+ const color = Util.normalizeColorType(mdOptions.color);
32
+ const maxCols = Util.normalizeMaxCols(mdOptions.maxCols, 15);
33
+
34
+ const mdPath = path.resolve(options.outputDir, mdOptions.outputFile);
35
+
36
+ const metrics = Util.getMetrics(mdOptions.metrics, reportData.type);
37
+
38
+ let files = reportData.files;
39
+ const skipPercent = mdOptions.skipPercent;
40
+ if (typeof skipPercent === 'number' && skipPercent > 0) {
41
+ files = files.filter((file) => {
42
+ const { summary } = file;
43
+ for (const k of metrics) {
44
+ const percent = summary[k].pct;
45
+ if (typeof percent === 'number' && percent < skipPercent) {
46
+ return true;
47
+ }
48
+ }
49
+ return false;
50
+ });
51
+ }
52
+
53
+ files = getFilteredFiles(mdOptions.filter, files);
54
+
55
+ const rows = [];
56
+ files.forEach((file) => {
57
+
58
+ // do NOT add debug file
59
+ if (file.debug) {
60
+ return;
61
+ }
62
+
63
+ const { sourcePath, summary } = file;
64
+
65
+ const fileRow = getRowData(sourcePath, summary, metrics, color);
66
+
67
+ let rowName = cutWithMaxCols(fileRow.name, maxCols, true);
68
+
69
+ if (fileRow.nameStatus) {
70
+ const nameColor = baseUrl && color === 'tex' ? '' : color;
71
+ rowName = Util.getColorStrByStatus(rowName, fileRow.nameStatus, nameColor);
72
+ }
73
+ if (baseUrl) {
74
+ rowName = `[${rowName}](${baseUrl + sourcePath})`;
75
+ }
76
+
77
+ fileRow.name = rowName;
78
+
79
+ const uncoveredLines = Util.getUncoveredLines(file.data.lines, color);
80
+ fileRow.uncoveredLines = cutWithMaxCols(uncoveredLines, maxCols);
81
+
82
+ rows.push(fileRow);
83
+ });
84
+
85
+ const summaryRow = getRowData('Summary', reportData.summary, metrics, color);
86
+ if (summaryRow.nameStatus) {
87
+ summaryRow.name = Util.getColorStrByStatus(summaryRow.name, summaryRow.nameStatus, color);
88
+ }
89
+ rows.push(summaryRow);
90
+
91
+ const columns = [{
92
+ id: 'name',
93
+ name: 'Name'
94
+ }, ... metrics.map((m) => {
95
+ return {
96
+ id: m,
97
+ name: Util.capitalizeFirstLetter(m),
98
+ align: 'right'
99
+ };
100
+ }), {
101
+ id: 'uncoveredLines',
102
+ name: 'Uncovered Lines'
103
+ }];
104
+
105
+ const markdownGrid = Util.markdownGrid({
106
+ options: {
107
+ name: reportData.name
108
+ },
109
+ columns,
110
+ rows
111
+ });
112
+
113
+ await Util.writeFile(mdPath, markdownGrid);
114
+ return Util.relativePath(mdPath);
115
+ };
116
+
117
+
118
+ module.exports = {
119
+ markdownDetailsReport
120
+ };
@@ -0,0 +1,44 @@
1
+ const path = require('path');
2
+ const Util = require('../utils/util.js');
3
+
4
+ const { getSummaryColumns } = require('./console-summary.js');
5
+
6
+ const markdownSummaryReport = async (reportData, reportOptions, options) => {
7
+ const msOptions = {
8
+ // unicode, html, or empty for no color
9
+ color: 'unicode',
10
+ metrics: [],
11
+ outputFile: 'coverage-summary.md',
12
+ ... reportOptions
13
+ };
14
+
15
+ const color = Util.normalizeColorType(msOptions.color);
16
+
17
+ const mdPath = path.resolve(options.outputDir, msOptions.outputFile);
18
+
19
+ const metrics = Util.getMetrics(msOptions.metrics, reportData.type);
20
+
21
+ const rows = metrics.map((k) => {
22
+ return {
23
+ ... reportData.summary[k],
24
+ name: Util.capitalizeFirstLetter(k)
25
+ };
26
+ });
27
+
28
+ const columns = getSummaryColumns(color);
29
+
30
+ const markdownGrid = Util.markdownGrid({
31
+ options: {
32
+ name: reportData.name
33
+ },
34
+ columns,
35
+ rows
36
+ });
37
+
38
+ await Util.writeFile(mdPath, markdownGrid);
39
+ return Util.relativePath(mdPath);
40
+ };
41
+
42
+ module.exports = {
43
+ markdownSummaryReport
44
+ };
@@ -0,0 +1,33 @@
1
+ const fs = require('fs');
2
+ const path = require('path');
3
+ const Util = require('../utils/util.js');
4
+
5
+ const rawReport = (reportData, reportOptions, options) => {
6
+ const rawOptions = {
7
+ outputDir: 'raw',
8
+ ... reportOptions
9
+ };
10
+
11
+ const cacheDir = options.cacheDir;
12
+ const rawDir = path.resolve(options.outputDir, rawOptions.outputDir);
13
+ // console.log(rawDir, cacheDir);
14
+ if (fs.existsSync(rawDir)) {
15
+ Util.logError(`Failed to save raw report because the dir already exists: ${Util.relativePath(rawDir)}`);
16
+ return;
17
+ }
18
+
19
+ const rawParent = path.dirname(rawDir);
20
+ if (!fs.existsSync(rawParent)) {
21
+ fs.mkdirSync(rawParent, {
22
+ recursive: true
23
+ });
24
+ }
25
+
26
+ // just rename the cache folder name
27
+ fs.renameSync(cacheDir, rawDir);
28
+
29
+ };
30
+
31
+ module.exports = {
32
+ rawReport
33
+ };
@@ -0,0 +1,22 @@
1
+ const path = require('path');
2
+ const Util = require('../utils/util.js');
3
+
4
+ const v8JsonReport = async (reportData, reportOptions, options) => {
5
+ const v8JsonOptions = {
6
+ outputFile: 'coverage-report.json',
7
+ ... reportOptions
8
+ };
9
+
10
+ // console.log(mergedOptions);
11
+ const jsonPath = path.resolve(options.outputDir, v8JsonOptions.outputFile);
12
+ const reportPath = Util.relativePath(jsonPath);
13
+
14
+ reportData.reportPath = reportPath;
15
+
16
+ await Util.writeFile(jsonPath, JSON.stringify(reportData));
17
+ return reportPath;
18
+ };
19
+
20
+ module.exports = {
21
+ v8JsonReport
22
+ };
@@ -0,0 +1,51 @@
1
+ const path = require('path');
2
+ const Assets = require('../assets.js');
3
+
4
+ const v8Report = async (reportData, reportOptions, options) => {
5
+
6
+ // V8 only options, merged with root options
7
+ const v8HtmlOptions = {
8
+ outputFile: options.outputFile,
9
+ inline: options.inline,
10
+ assetsPath: options.assetsPath,
11
+ metrics: options.metrics,
12
+ ... reportOptions
13
+ };
14
+
15
+ // add metrics to data for UI
16
+ reportData.metrics = v8HtmlOptions.metrics;
17
+
18
+ const {
19
+ outputFile, inline, assetsPath
20
+ } = v8HtmlOptions;
21
+
22
+ // resolve full report path
23
+ const reportPath = path.resolve(options.outputDir, outputFile);
24
+ const outputDir = path.dirname(reportPath);
25
+ const htmlFile = path.basename(reportPath);
26
+
27
+ const jsFiles = ['monocart-coverage-app'];
28
+ // console.log(jsFiles);
29
+
30
+ const htmlOptions = {
31
+ reportData,
32
+ jsFiles,
33
+ inline,
34
+ assetsPath,
35
+ outputDir,
36
+ htmlFile,
37
+
38
+ saveReportPath: 'reportPath',
39
+
40
+ reportDataFile: 'coverage-data.js'
41
+ };
42
+
43
+ // compress json data for html
44
+ const htmlPath = await Assets.saveHtmlReport(htmlOptions);
45
+
46
+ return htmlPath;
47
+ };
48
+
49
+ module.exports = {
50
+ v8Report
51
+ };