monocart-coverage-reports 2.8.6 → 2.8.7
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 +18 -0
- package/README.zh-Hans.md +18 -0
- package/lib/converter/converter.js +1 -2
- package/lib/generate.js +83 -40
- package/lib/index.d.ts +4 -1
- package/lib/index.js +13 -5
- package/lib/packages/monocart-coverage-app.js +1 -1
- package/lib/utils/util.js +10 -1
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -35,6 +35,7 @@
|
|
|
35
35
|
* [Common issues](#common-issues)
|
|
36
36
|
- [Unexpected coverage](#unexpected-coverage)
|
|
37
37
|
- [Unparsable source](#unparsable-source)
|
|
38
|
+
- [JavaScript heap out of memory](#javascript-heap-out-of-memory)
|
|
38
39
|
* [Debug for Coverage and Sourcemap](#debug-for-coverage-and-sourcemap)
|
|
39
40
|
* [Integration with Any Testing Framework](#integration-with-any-testing-framework)
|
|
40
41
|
* [Integration Examples](#integration-examples)
|
|
@@ -141,6 +142,9 @@ For more information, see [Command Line](#command-line)
|
|
|
141
142
|
- `codecov`
|
|
142
143
|
- coverage data for [Codecov](https://docs.codecov.com/docs/codecov-custom-coverage-format), see [example](https://app.codecov.io/github/cenfun/monocart-coverage-reports)
|
|
143
144
|
|
|
145
|
+
- `codacy`
|
|
146
|
+
- coverage data for [Codacy](https://api.codacy.com/swagger#tocscoveragereport) API
|
|
147
|
+
|
|
144
148
|
- `console-summary` shows coverage summary in the console
|
|
145
149
|
|
|
146
150
|

|
|
@@ -881,6 +885,15 @@ const coverageOptions = {
|
|
|
881
885
|
}
|
|
882
886
|
```
|
|
883
887
|
|
|
888
|
+
### JavaScript heap out of memory
|
|
889
|
+
When there are a lot of raw v8 coverage files to process, it may cause OOM. We can try the following Node.js options:
|
|
890
|
+
```sh
|
|
891
|
+
- run: npm run test:coverage
|
|
892
|
+
env:
|
|
893
|
+
NODE_OPTIONS: --max-old-space-size=8192
|
|
894
|
+
```
|
|
895
|
+
|
|
896
|
+
|
|
884
897
|
## Debug for Coverage and Sourcemap
|
|
885
898
|
> Sometimes, the coverage is not what we expect. The next step is to figure out why, and we can easily find out the answer step by step through debugging.
|
|
886
899
|
- Start debugging for v8 report with option `logging: 'debug'`
|
|
@@ -900,6 +913,11 @@ When `logging` is `debug`, the raw report data will be preserved in `[outputDir]
|
|
|
900
913
|
|
|
901
914
|

|
|
902
915
|
|
|
916
|
+
- Show time logs with env `MCR_LOG_TIME`
|
|
917
|
+
```js
|
|
918
|
+
process.env.MCR_LOG_TIME = true
|
|
919
|
+
```
|
|
920
|
+
|
|
903
921
|
## Integration with Any Testing Framework
|
|
904
922
|
- API
|
|
905
923
|
- Collecting coverage data when any stage of the test is completed, and adding the coverage data to the coverage reporter. `await mcr.add(coverageData)`
|
package/README.zh-Hans.md
CHANGED
|
@@ -35,6 +35,7 @@
|
|
|
35
35
|
* [常见问题](#common-issues)
|
|
36
36
|
- [Unexpected coverage](#unexpected-coverage)
|
|
37
37
|
- [Unparsable source](#unparsable-source)
|
|
38
|
+
- [JavaScript heap out of memory](#javascript-heap-out-of-memory)
|
|
38
39
|
* [如何调试覆盖率数据和查看sourcemap](#debug-for-coverage-and-sourcemap)
|
|
39
40
|
* [如何跟其他框架集成](#integration-with-any-testing-framework)
|
|
40
41
|
* [集成的例子](#integration-examples)
|
|
@@ -141,6 +142,9 @@ mcr node my-app.js -r v8,console-details
|
|
|
141
142
|
- `codecov`
|
|
142
143
|
- 专属支持[Codecov](https://docs.codecov.com/docs/codecov-custom-coverage-format)的报告, see [example](https://app.codecov.io/github/cenfun/monocart-coverage-reports)
|
|
143
144
|
|
|
145
|
+
- `codacy`
|
|
146
|
+
- 专属支持[Codacy](https://api.codacy.com/swagger#tocscoveragereport) API的报告
|
|
147
|
+
|
|
144
148
|
- `console-summary` 在控制台显示覆盖率概要
|
|
145
149
|
|
|
146
150
|

|
|
@@ -884,6 +888,15 @@ const coverageOptions = {
|
|
|
884
888
|
}
|
|
885
889
|
```
|
|
886
890
|
|
|
891
|
+
### JavaScript heap out of memory
|
|
892
|
+
内存溢出问题可能出现在有太多的原生V8覆盖率文件要处理. 我们可以使用Node.js的一个选项来增加内存使用:
|
|
893
|
+
```sh
|
|
894
|
+
- run: npm run test:coverage
|
|
895
|
+
env:
|
|
896
|
+
NODE_OPTIONS: --max-old-space-size=8192
|
|
897
|
+
```
|
|
898
|
+
|
|
899
|
+
|
|
887
900
|
## Debug for Coverage and Sourcemap
|
|
888
901
|
> 当你觉得覆盖率存在问题的时候,`MCR`支持自行调试来核验覆盖率的准确性
|
|
889
902
|
- 首先打开调试设置`logging: 'debug'`
|
|
@@ -903,6 +916,11 @@ const coverageOptions = {
|
|
|
903
916
|
|
|
904
917
|

|
|
905
918
|
|
|
919
|
+
- 使用环境变量`MCR_LOG_TIME`显示时间日志
|
|
920
|
+
```js
|
|
921
|
+
process.env.MCR_LOG_TIME = true
|
|
922
|
+
```
|
|
923
|
+
|
|
906
924
|
## Integration with Any Testing Framework
|
|
907
925
|
通用集成方案
|
|
908
926
|
- 通过API接口在程序集成
|
|
@@ -147,8 +147,7 @@ const handleLinesCoverage = (bytes, locator, ignoredRanges) => {
|
|
|
147
147
|
blank: blankCount,
|
|
148
148
|
comment: commentCount
|
|
149
149
|
};
|
|
150
|
-
// data lines
|
|
151
|
-
// https://docs.codecov.com/docs/codecov-custom-coverage-format
|
|
150
|
+
// data lines
|
|
152
151
|
const dataLines = {};
|
|
153
152
|
|
|
154
153
|
// no ignore items
|
package/lib/generate.js
CHANGED
|
@@ -90,6 +90,7 @@ const getReportGroup = (reports, lcov, dataType) => {
|
|
|
90
90
|
|
|
91
91
|
// both
|
|
92
92
|
'codecov': 'both',
|
|
93
|
+
'codacy': 'both',
|
|
93
94
|
'console-details': 'both',
|
|
94
95
|
'console-summary': 'both',
|
|
95
96
|
'raw': 'both'
|
|
@@ -280,6 +281,42 @@ const handleCodecovReport = async (reportData, reportOptions, options) => {
|
|
|
280
281
|
return Util.relativePath(jsonPath);
|
|
281
282
|
};
|
|
282
283
|
|
|
284
|
+
const handleCodacyReport = async (reportData, reportOptions, options) => {
|
|
285
|
+
const codacyOptions = {
|
|
286
|
+
outputFile: 'codacy.json',
|
|
287
|
+
... reportOptions
|
|
288
|
+
};
|
|
289
|
+
|
|
290
|
+
const jsonPath = path.resolve(options.outputDir, codacyOptions.outputFile);
|
|
291
|
+
|
|
292
|
+
// https://api.codacy.com/swagger#tocscoveragereport
|
|
293
|
+
const fileReports = [];
|
|
294
|
+
reportData.files.forEach((item) => {
|
|
295
|
+
const { sourcePath, data } = item;
|
|
296
|
+
|
|
297
|
+
const coverage = {};
|
|
298
|
+
for (const [key, value] of Object.entries(data.lines)) {
|
|
299
|
+
if (typeof value === 'number') {
|
|
300
|
+
coverage[key] = value;
|
|
301
|
+
} else {
|
|
302
|
+
// partial coverage not supported?
|
|
303
|
+
coverage[key] = 0;
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
fileReports.push({
|
|
308
|
+
filename: sourcePath,
|
|
309
|
+
coverage
|
|
310
|
+
});
|
|
311
|
+
});
|
|
312
|
+
const codacyData = {
|
|
313
|
+
fileReports
|
|
314
|
+
};
|
|
315
|
+
|
|
316
|
+
await Util.writeFile(jsonPath, JSON.stringify(codacyData));
|
|
317
|
+
return Util.relativePath(jsonPath);
|
|
318
|
+
};
|
|
319
|
+
|
|
283
320
|
const handleConsoleDetailsReport = (reportData, reportOptions, options) => {
|
|
284
321
|
const cdOptions = {
|
|
285
322
|
maxCols: 50,
|
|
@@ -464,6 +501,7 @@ const generateCoverageReports = async (dataList, sourceCache, options) => {
|
|
|
464
501
|
|
|
465
502
|
const buildInBothReports = {
|
|
466
503
|
'codecov': handleCodecovReport,
|
|
504
|
+
'codacy': handleCodacyReport,
|
|
467
505
|
'console-details': handleConsoleDetailsReport,
|
|
468
506
|
'console-summary': handleConsoleSummaryReport,
|
|
469
507
|
'raw': handleRawReport
|
|
@@ -854,9 +892,16 @@ const resolveEntrySourceMap = (entry, sourceMapCache) => {
|
|
|
854
892
|
}
|
|
855
893
|
};
|
|
856
894
|
|
|
857
|
-
const readCoverageData = (
|
|
895
|
+
const readCoverageData = async (dir, filename, entryFilter, sourceCache) => {
|
|
858
896
|
|
|
859
|
-
const
|
|
897
|
+
const content = await Util.readFile(path.resolve(dir, filename));
|
|
898
|
+
if (!content) {
|
|
899
|
+
return;
|
|
900
|
+
}
|
|
901
|
+
const json = JSON.parse(content);
|
|
902
|
+
if (!json) {
|
|
903
|
+
return;
|
|
904
|
+
}
|
|
860
905
|
|
|
861
906
|
// raw v8 json
|
|
862
907
|
let coverageData = json.result;
|
|
@@ -886,6 +931,26 @@ const readCoverageData = (item, entryFilter, sourceCache) => {
|
|
|
886
931
|
return coverageData;
|
|
887
932
|
};
|
|
888
933
|
|
|
934
|
+
const readSourceList = async (dir, sourceList) => {
|
|
935
|
+
const sourceCache = {};
|
|
936
|
+
|
|
937
|
+
for (const filename of sourceList) {
|
|
938
|
+
const content = await Util.readFile(path.resolve(dir, filename));
|
|
939
|
+
if (!content) {
|
|
940
|
+
continue;
|
|
941
|
+
}
|
|
942
|
+
const json = JSON.parse(content);
|
|
943
|
+
if (!json) {
|
|
944
|
+
continue;
|
|
945
|
+
}
|
|
946
|
+
if (json.url) {
|
|
947
|
+
sourceCache[json.url] = json;
|
|
948
|
+
}
|
|
949
|
+
}
|
|
950
|
+
|
|
951
|
+
return sourceCache;
|
|
952
|
+
};
|
|
953
|
+
|
|
889
954
|
const readFromDir = async (mcr, dir) => {
|
|
890
955
|
|
|
891
956
|
if (!dir || !fs.existsSync(dir)) {
|
|
@@ -894,56 +959,34 @@ const readFromDir = async (mcr, dir) => {
|
|
|
894
959
|
}
|
|
895
960
|
|
|
896
961
|
const files = fs.readdirSync(dir);
|
|
897
|
-
|
|
962
|
+
|
|
963
|
+
const coverageList = [];
|
|
964
|
+
const sourceList = [];
|
|
965
|
+
|
|
898
966
|
files.forEach((filename) => {
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
list.push({
|
|
908
|
-
type: 'source',
|
|
909
|
-
filename
|
|
910
|
-
});
|
|
967
|
+
// read all json files
|
|
968
|
+
if (filename.endsWith('.json')) {
|
|
969
|
+
// could be source files generated by register hooks
|
|
970
|
+
if (filename.startsWith('source-')) {
|
|
971
|
+
sourceList.push(filename);
|
|
972
|
+
} else {
|
|
973
|
+
coverageList.push(filename);
|
|
974
|
+
}
|
|
911
975
|
}
|
|
912
976
|
});
|
|
913
977
|
|
|
914
|
-
if (!
|
|
978
|
+
if (!coverageList.length) {
|
|
915
979
|
Util.logInfo(`No coverage files in the dir: ${dir}`);
|
|
916
980
|
return;
|
|
917
981
|
}
|
|
918
982
|
|
|
919
|
-
const
|
|
920
|
-
const sourceCache = {};
|
|
921
|
-
const coverageFiles = [];
|
|
922
|
-
|
|
923
|
-
contentList.forEach((content, i) => {
|
|
924
|
-
if (!content) {
|
|
925
|
-
return;
|
|
926
|
-
}
|
|
927
|
-
const json = JSON.parse(content);
|
|
928
|
-
if (!json) {
|
|
929
|
-
return;
|
|
930
|
-
}
|
|
931
|
-
const item = list[i];
|
|
932
|
-
if (item.type === 'source') {
|
|
933
|
-
if (json.url) {
|
|
934
|
-
sourceCache[json.url] = json;
|
|
935
|
-
}
|
|
936
|
-
} else {
|
|
937
|
-
item.json = json;
|
|
938
|
-
coverageFiles.push(item);
|
|
939
|
-
}
|
|
940
|
-
});
|
|
983
|
+
const sourceCache = await readSourceList(dir, sourceList);
|
|
941
984
|
|
|
942
985
|
const entryFilter = mcr.getEntryFilter();
|
|
943
986
|
|
|
944
987
|
const results = [];
|
|
945
|
-
for (const
|
|
946
|
-
const coverageData = readCoverageData(
|
|
988
|
+
for (const filename of coverageList) {
|
|
989
|
+
const coverageData = await readCoverageData(dir, filename, entryFilter, sourceCache);
|
|
947
990
|
if (coverageData) {
|
|
948
991
|
const res = await mcr.add(coverageData);
|
|
949
992
|
results.push(res);
|
package/lib/index.d.ts
CHANGED
|
@@ -107,6 +107,9 @@ declare namespace MCR {
|
|
|
107
107
|
['codecov'] | ["codecov", {
|
|
108
108
|
outputFile?: string;
|
|
109
109
|
}] |
|
|
110
|
+
['codacy'] | ["codacy", {
|
|
111
|
+
outputFile?: string;
|
|
112
|
+
}] |
|
|
110
113
|
['console-summary'] | ['console-summary', {
|
|
111
114
|
metrics?: Array<"bytes" | "statements" | "branches" | "functions" | "lines">;
|
|
112
115
|
}] |
|
|
@@ -190,7 +193,7 @@ declare namespace MCR {
|
|
|
190
193
|
branches?: CoverageRange[];
|
|
191
194
|
functions?: CoverageRange[];
|
|
192
195
|
ignores?: IgnoredRange[];
|
|
193
|
-
/**
|
|
196
|
+
/** common json format */
|
|
194
197
|
lines: {
|
|
195
198
|
[key: string]: number | string;
|
|
196
199
|
};
|
package/lib/index.js
CHANGED
|
@@ -141,6 +141,9 @@ class CoverageReport {
|
|
|
141
141
|
return;
|
|
142
142
|
}
|
|
143
143
|
|
|
144
|
+
// GC, no OOM
|
|
145
|
+
this.fileCache.clear();
|
|
146
|
+
|
|
144
147
|
const { dataList, sourceCache } = inputData;
|
|
145
148
|
|
|
146
149
|
// empty output dir except cache dir before generate reports
|
|
@@ -161,16 +164,21 @@ class CoverageReport {
|
|
|
161
164
|
|
|
162
165
|
const coverageResults = await generateCoverageReports(dataList, sourceCache, this.options);
|
|
163
166
|
|
|
164
|
-
if (this.logging !== 'debug') {
|
|
165
|
-
Util.rmSync(this.options.cacheDir);
|
|
166
|
-
}
|
|
167
|
-
|
|
168
167
|
const onEnd = this.options.onEnd;
|
|
169
168
|
if (typeof onEnd === 'function') {
|
|
170
169
|
await onEnd.call(this, coverageResults);
|
|
171
170
|
}
|
|
172
171
|
|
|
173
|
-
|
|
172
|
+
// remove cache dir after finished
|
|
173
|
+
if (!Util.isDebug()) {
|
|
174
|
+
Util.rmSync(this.options.cacheDir);
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
let reportPath = '';
|
|
178
|
+
if (coverageResults.reportPath) {
|
|
179
|
+
reportPath = EC.cyan(coverageResults.reportPath);
|
|
180
|
+
}
|
|
181
|
+
Util.logTime(`generated coverage reports: ${reportPath}`, time_start);
|
|
174
182
|
|
|
175
183
|
return coverageResults;
|
|
176
184
|
}
|