monocart-coverage-reports 2.5.6 → 2.5.8
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 +90 -43
- package/lib/converter/ast.js +18 -3
- package/lib/converter/collect-source-maps.js +2 -15
- package/lib/converter/converter.js +26 -7
- package/lib/default/options.js +7 -0
- package/lib/generate.js +200 -1
- package/lib/index.d.ts +34 -6
- package/lib/index.js +9 -6
- package/lib/istanbul/istanbul.js +2 -2
- package/lib/packages/monocart-coverage-v8.js +1 -1
- package/lib/packages/monocart-coverage-vendor.js +46 -57
- package/lib/utils/request.js +62 -0
- package/lib/utils/source-path.js +25 -2
- package/lib/utils/util.js +5 -10
- package/lib/v8/v8-summary.js +8 -0
- package/lib/v8/v8.js +26 -8
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -37,6 +37,8 @@
|
|
|
37
37
|
- [Playwright](#playwright)
|
|
38
38
|
- [Jest](#jest)
|
|
39
39
|
- [Vitest](#vitest)
|
|
40
|
+
- [CodeceptJS](#codeceptjs)
|
|
41
|
+
- [WebdriverIO](#webdriverio)
|
|
40
42
|
- [Codecov](#codecov)
|
|
41
43
|
- [Coveralls](#coveralls)
|
|
42
44
|
- [Sonar Cloud](#sonar-cloud)
|
|
@@ -190,12 +192,22 @@ const coverageOptions = {
|
|
|
190
192
|
sourceFilter: (sourcePath) => sourcePath.search(/src\//) !== -1
|
|
191
193
|
};
|
|
192
194
|
```
|
|
193
|
-
Or `minimatch` pattern:
|
|
195
|
+
Or using `minimatch` pattern:
|
|
194
196
|
```js
|
|
195
197
|
const coverageOptions = {
|
|
196
198
|
entryFilter: "**/main.js",
|
|
197
199
|
sourceFilter: "**/src/**"
|
|
198
200
|
};
|
|
201
|
+
// supports multiple patterns:
|
|
202
|
+
const coverageOptions = {
|
|
203
|
+
entryFilter: {
|
|
204
|
+
'**/vendor.js': false,
|
|
205
|
+
'**/main.js': true
|
|
206
|
+
},
|
|
207
|
+
sourceFilter: {
|
|
208
|
+
'**/src/**': true
|
|
209
|
+
}
|
|
210
|
+
};
|
|
199
211
|
```
|
|
200
212
|
|
|
201
213
|
## onEnd Hook
|
|
@@ -343,21 +355,31 @@ await MCR(coverageOptions).add(jsCoverage);
|
|
|
343
355
|
## Collecting Raw V8 Coverage Data with Puppeteer
|
|
344
356
|
> Puppeteer does not provide raw v8 coverage data by default. A simple conversion is required, see example: [./test/test-puppeteer.js](./test/test-puppeteer.js)
|
|
345
357
|
```js
|
|
346
|
-
await
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
358
|
+
await Promise.all([
|
|
359
|
+
page.coverage.startJSCoverage({
|
|
360
|
+
resetOnNavigation: false,
|
|
361
|
+
// provide raw v8 coverage data
|
|
362
|
+
includeRawScriptCoverage: true
|
|
363
|
+
}),
|
|
364
|
+
page.coverage.startCSSCoverage({
|
|
365
|
+
resetOnNavigation: false
|
|
366
|
+
})
|
|
367
|
+
]);
|
|
350
368
|
|
|
351
369
|
await page.goto(url);
|
|
352
370
|
|
|
353
|
-
const jsCoverage = await
|
|
354
|
-
|
|
355
|
-
|
|
371
|
+
const [jsCoverage, cssCoverage] = await Promise.all([
|
|
372
|
+
page.coverage.stopJSCoverage(),
|
|
373
|
+
page.coverage.stopCSSCoverage()
|
|
374
|
+
]);
|
|
375
|
+
|
|
376
|
+
// to raw V8 script coverage
|
|
377
|
+
const coverageData = [... jsCoverage.map((it) => {
|
|
356
378
|
return {
|
|
357
379
|
source: it.text,
|
|
358
380
|
... it.rawScriptCoverage
|
|
359
381
|
};
|
|
360
|
-
}
|
|
382
|
+
}), ... cssCoverage];
|
|
361
383
|
```
|
|
362
384
|
|
|
363
385
|
## Node.js V8 Coverage Report for Server Side
|
|
@@ -492,45 +514,52 @@ const coverageOptions = {
|
|
|
492
514
|
}
|
|
493
515
|
};
|
|
494
516
|
```
|
|
495
|
-
|
|
517
|
+
It also supports simple key/value replacement:
|
|
518
|
+
```js
|
|
519
|
+
const coverageOptions = {
|
|
520
|
+
sourcePath: {
|
|
521
|
+
'my-dist-file1/': '',
|
|
522
|
+
'my-dist-file2/': ''
|
|
523
|
+
}
|
|
524
|
+
};
|
|
525
|
+
```
|
|
496
526
|
|
|
497
527
|
## Adding Empty Coverage for Untested Files
|
|
498
|
-
By default the untested files will not be included in the coverage report, we can
|
|
528
|
+
By default the untested files will not be included in the coverage report, we can add empty coverage data for all files with option `all`, the untested files will show 0% coverage.
|
|
499
529
|
```js
|
|
500
|
-
const
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
const coverageData = [];
|
|
507
|
-
const fileList = fs.readdirSync(dir);
|
|
508
|
-
for (const filename of fileList) {
|
|
509
|
-
const filePath = path.resolve(dir, filename);
|
|
510
|
-
const source = fs.readFileSync(filePath).toString('utf-8');
|
|
511
|
-
const sourcePath = path.relative(process.cwd(), filePath);
|
|
512
|
-
const url = pathToFileURL(sourcePath).toString();
|
|
513
|
-
const extname = path.extname(filename);
|
|
514
|
-
if (['.css'].includes(extname)) {
|
|
515
|
-
coverageData.push({
|
|
516
|
-
empty: true,
|
|
517
|
-
type: 'css',
|
|
518
|
-
url,
|
|
519
|
-
text: source
|
|
520
|
-
});
|
|
521
|
-
} else {
|
|
522
|
-
coverageData.push({
|
|
523
|
-
empty: true,
|
|
524
|
-
type: 'js',
|
|
525
|
-
url,
|
|
526
|
-
source
|
|
527
|
-
});
|
|
530
|
+
const coverageOptions = {
|
|
531
|
+
all: {
|
|
532
|
+
dir: ['./src'],
|
|
533
|
+
filter: (filePath) => {
|
|
534
|
+
return true;
|
|
535
|
+
}
|
|
528
536
|
}
|
|
529
|
-
}
|
|
530
|
-
|
|
531
|
-
|
|
537
|
+
};
|
|
538
|
+
```
|
|
539
|
+
The filter also supports `minimatch` pattern:
|
|
540
|
+
```js
|
|
541
|
+
const coverageOptions = {
|
|
542
|
+
all: {
|
|
543
|
+
dir: ['./src'],
|
|
544
|
+
filter: '**/*.js'
|
|
545
|
+
}
|
|
546
|
+
};
|
|
547
|
+
// or multiple patterns
|
|
548
|
+
const coverageOptions = {
|
|
549
|
+
all: {
|
|
550
|
+
dir: ['./src'],
|
|
551
|
+
filter: {
|
|
552
|
+
// exclude files
|
|
553
|
+
'**/ignored-*.js': false,
|
|
554
|
+
'**/*.html': false,
|
|
555
|
+
'**/*.ts': false,
|
|
556
|
+
// empty css coverage
|
|
557
|
+
'**/*.scss': "css",
|
|
558
|
+
'**/*': true
|
|
559
|
+
}
|
|
560
|
+
}
|
|
561
|
+
};
|
|
532
562
|
```
|
|
533
|
-
see example: [./test/cli-options.js](./test/cli-options.js)
|
|
534
563
|
|
|
535
564
|
## Ignoring Uncovered Codes
|
|
536
565
|
To ignore codes, use the special comment which starts with `v8 ignore `:
|
|
@@ -649,12 +678,30 @@ When `logging` is `debug`, the raw report data will be preserved in `[outputDir]
|
|
|
649
678
|

|
|
650
679
|
|
|
651
680
|
## Integration
|
|
681
|
+
|
|
652
682
|
### [Playwright](https://github.com/microsoft/playwright)
|
|
653
683
|
- [monocart-reporter](https://github.com/cenfun/monocart-reporter) - A Playwright custom reporter, supports generating [Code Coverage Report](https://github.com/cenfun/monocart-reporter?#code-coverage-report)
|
|
684
|
+
- Coverage for component testing:
|
|
685
|
+
- [playwright-ct-vue](https://github.com/cenfun/playwright-ct-vue)
|
|
686
|
+
- [playwright-ct-react](https://github.com/cenfun/playwright-ct-react)
|
|
687
|
+
- [playwright-ct-svelte](https://github.com/cenfun/playwright-ct-svelte)
|
|
688
|
+
- Coverage for Next.js, both server side and client side:
|
|
689
|
+
- [nextjs-with-playwright](https://github.com/cenfun/nextjs-with-playwright)
|
|
690
|
+
- [nextjs-with-playwright-istanbul](https://github.com/cenfun/nextjs-with-playwright-istanbul)
|
|
691
|
+
|
|
654
692
|
### [Jest](https://github.com/jestjs/jest/)
|
|
655
693
|
- [jest-monocart-coverage](https://github.com/cenfun/jest-monocart-coverage) - A Jest custom reporter for coverage reports
|
|
694
|
+
- Example for Jest (unit) + Puppeteer (e2e) + Codecov: [maplibre-gl-js](https://github.com/maplibre/maplibre-gl-js)
|
|
695
|
+
|
|
656
696
|
### [Vitest](https://github.com/vitest-dev/vitest)
|
|
657
697
|
- [vitest-monocart-coverage](https://github.com/cenfun/vitest-monocart-coverage) - A Vitest custom provider module for coverage reports
|
|
698
|
+
|
|
699
|
+
### [CodeceptJS](https://github.com/codeceptjs/CodeceptJS)
|
|
700
|
+
- [codeceptjs-monocart-coverage](https://github.com/cenfun/codeceptjs-monocart-coverage) - A CodeceptJS plugin for coverage reports
|
|
701
|
+
|
|
702
|
+
### [WebdriverIO](https://github.com/webdriverio/webdriverio)
|
|
703
|
+
- [wdio-monocart-service](https://github.com/cenfun/wdio-monocart-service) - A WebdriverIO service for coverage reports
|
|
704
|
+
|
|
658
705
|
### [Codecov](https://codecov.com/)
|
|
659
706
|
[](https://codecov.io/gh/cenfun/monocart-coverage-reports)
|
|
660
707
|
- Supports native `codecov` built-in report ([specification](https://docs.codecov.com/docs/codecov-custom-coverage-format))
|
package/lib/converter/ast.js
CHANGED
|
@@ -167,19 +167,34 @@ const getJsAstInfo = (item, coverageList) => {
|
|
|
167
167
|
allowHashBang: true
|
|
168
168
|
};
|
|
169
169
|
|
|
170
|
+
let err;
|
|
170
171
|
let ast;
|
|
171
172
|
try {
|
|
172
173
|
ast = acorn.parse(source, options);
|
|
173
174
|
} catch (e) {
|
|
175
|
+
err = e;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
if (err) {
|
|
174
179
|
|
|
175
|
-
// empty = untested file
|
|
176
|
-
|
|
177
|
-
|
|
180
|
+
// empty = untested file
|
|
181
|
+
// could be .ts, .vue and so on, that can not be parsed normally
|
|
182
|
+
if (empty) {
|
|
183
|
+
return {
|
|
184
|
+
functions: [],
|
|
185
|
+
branches: [],
|
|
186
|
+
statements: []
|
|
187
|
+
};
|
|
178
188
|
}
|
|
189
|
+
|
|
190
|
+
// runtime code should be parsed successful
|
|
191
|
+
Util.logInfo(`failed to parse source file: ${item.sourcePath} ${err.message}`);
|
|
192
|
+
|
|
179
193
|
// https://github.com/acornjs/acorn/tree/master/acorn-loose
|
|
180
194
|
// It is recommended to always try a parse with the regular acorn parser first,
|
|
181
195
|
// and only fall back to this parser when that one finds syntax errors.
|
|
182
196
|
ast = acornLoose.parse(source, options);
|
|
197
|
+
|
|
183
198
|
}
|
|
184
199
|
|
|
185
200
|
const coverageInfo = getCoverageInfo(coverageList);
|
|
@@ -3,23 +3,10 @@ const path = require('path');
|
|
|
3
3
|
const EC = require('eight-colors');
|
|
4
4
|
const { fileURLToPath, pathToFileURL } = require('url');
|
|
5
5
|
const Concurrency = require('../platform/concurrency.js');
|
|
6
|
-
const { convertSourceMap
|
|
6
|
+
const { convertSourceMap } = require('../packages/monocart-coverage-vendor.js');
|
|
7
7
|
|
|
8
8
|
const Util = require('../utils/util.js');
|
|
9
9
|
|
|
10
|
-
const request = async (options) => {
|
|
11
|
-
if (typeof options === 'string') {
|
|
12
|
-
options = {
|
|
13
|
-
url: options
|
|
14
|
-
};
|
|
15
|
-
}
|
|
16
|
-
let err;
|
|
17
|
-
const res = await axios(options).catch((e) => {
|
|
18
|
-
err = e;
|
|
19
|
-
});
|
|
20
|
-
return [err, res];
|
|
21
|
-
};
|
|
22
|
-
|
|
23
10
|
const loadSourceMap = async (url = '') => {
|
|
24
11
|
|
|
25
12
|
if (url.startsWith('file:')) {
|
|
@@ -32,7 +19,7 @@ const loadSourceMap = async (url = '') => {
|
|
|
32
19
|
return JSON.parse(content);
|
|
33
20
|
}
|
|
34
21
|
|
|
35
|
-
const [err, res] = await request({
|
|
22
|
+
const [err, res] = await Util.request({
|
|
36
23
|
url
|
|
37
24
|
});
|
|
38
25
|
|
|
@@ -14,7 +14,9 @@ const { getIgnoredRanges } = require('./ignore.js');
|
|
|
14
14
|
const {
|
|
15
15
|
sortRanges, dedupeCountRanges, mergeRangesWith
|
|
16
16
|
} = require('../utils/dedupe.js');
|
|
17
|
-
const {
|
|
17
|
+
const {
|
|
18
|
+
getSourceType, getSourcePathHandler, initSourceMapSourcePath
|
|
19
|
+
} = require('../utils/source-path.js');
|
|
18
20
|
const { decode, minimatch } = require('../packages/monocart-coverage-vendor.js');
|
|
19
21
|
|
|
20
22
|
const InfoBranch = require('./info-branch.js');
|
|
@@ -839,17 +841,34 @@ const getOriginalDecodedMappings = (originalDecodedMap, sourceIndex, locator) =>
|
|
|
839
841
|
// ========================================================================================================
|
|
840
842
|
|
|
841
843
|
const getSourceFilter = (options) => {
|
|
842
|
-
const
|
|
843
|
-
|
|
844
|
-
|
|
844
|
+
const input = options.sourceFilter;
|
|
845
|
+
|
|
846
|
+
// for function handler
|
|
847
|
+
if (typeof input === 'function') {
|
|
848
|
+
return input;
|
|
849
|
+
}
|
|
850
|
+
|
|
851
|
+
// for single minimatch pattern
|
|
852
|
+
if (input && typeof input === 'string') {
|
|
853
|
+
return (sourcePath) => {
|
|
854
|
+
return minimatch(sourcePath, input);
|
|
855
|
+
};
|
|
845
856
|
}
|
|
846
857
|
|
|
847
|
-
|
|
858
|
+
// for patterns
|
|
859
|
+
if (input && typeof input === 'object') {
|
|
860
|
+
const patterns = Object.keys(input);
|
|
848
861
|
return (sourcePath) => {
|
|
849
|
-
|
|
862
|
+
for (const pattern of patterns) {
|
|
863
|
+
if (minimatch(sourcePath, pattern)) {
|
|
864
|
+
return input[pattern];
|
|
865
|
+
}
|
|
866
|
+
}
|
|
867
|
+
// false if not matched
|
|
850
868
|
};
|
|
851
869
|
}
|
|
852
870
|
|
|
871
|
+
// default
|
|
853
872
|
return () => true;
|
|
854
873
|
};
|
|
855
874
|
|
|
@@ -968,7 +987,7 @@ const unpackSourceMap = (state, options) => {
|
|
|
968
987
|
|
|
969
988
|
// keep original urls
|
|
970
989
|
const fileUrls = {};
|
|
971
|
-
const sourcePathHandler = options
|
|
990
|
+
const sourcePathHandler = getSourcePathHandler(options);
|
|
972
991
|
initSourceMapSourcePath(sourceMap, fileUrls, sourcePathHandler);
|
|
973
992
|
state.fileUrls = fileUrls;
|
|
974
993
|
|
package/lib/default/options.js
CHANGED
|
@@ -46,6 +46,13 @@ module.exports = {
|
|
|
46
46
|
// sourceFilter: (sourcePath) => sourcePath.search(/src\/.+/) !== -1,
|
|
47
47
|
sourceFilter: null,
|
|
48
48
|
|
|
49
|
+
// options for adding empty coverage for all files
|
|
50
|
+
// all: {
|
|
51
|
+
// dir: ['src'],
|
|
52
|
+
// filter: (sourcePath) => true
|
|
53
|
+
// },
|
|
54
|
+
all: null,
|
|
55
|
+
|
|
49
56
|
// (V8 only) {boolean} Enable/Disable ignoring uncovered codes with the special comments: v8 ignore next/next N/start/stop
|
|
50
57
|
v8Ignore: true,
|
|
51
58
|
|
package/lib/generate.js
CHANGED
|
@@ -11,6 +11,9 @@ const { mergeV8Coverage, saveV8Report } = require('./v8/v8.js');
|
|
|
11
11
|
|
|
12
12
|
const { convertV8List } = require('./converter/converter.js');
|
|
13
13
|
|
|
14
|
+
const { minimatch } = require('./packages/monocart-coverage-vendor.js');
|
|
15
|
+
const { pathToFileURL } = require('url');
|
|
16
|
+
|
|
14
17
|
// ========================================================================================================
|
|
15
18
|
|
|
16
19
|
// maybe v8 or to istanbul reports
|
|
@@ -288,8 +291,175 @@ const generateCoverageReports = async (dataList, sourceCache, options) => {
|
|
|
288
291
|
|
|
289
292
|
// ========================================================================================================
|
|
290
293
|
|
|
294
|
+
const resolveAllDirList = (input) => {
|
|
295
|
+
const dirs = Util.toList(input, ',');
|
|
296
|
+
if (!dirs.length) {
|
|
297
|
+
return;
|
|
298
|
+
}
|
|
299
|
+
const dirList = dirs.filter((it) => fs.existsSync(it));
|
|
300
|
+
if (!dirList.length) {
|
|
301
|
+
return;
|
|
302
|
+
}
|
|
303
|
+
return dirList;
|
|
304
|
+
};
|
|
305
|
+
|
|
306
|
+
const resolveAllFilter = (input) => {
|
|
307
|
+
// for function handler
|
|
308
|
+
if (typeof input === 'function') {
|
|
309
|
+
return input;
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
// for single minimatch pattern
|
|
313
|
+
if (input && typeof input === 'string') {
|
|
314
|
+
return (filePath) => {
|
|
315
|
+
return minimatch(filePath, input);
|
|
316
|
+
};
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
// for patterns
|
|
320
|
+
if (input && typeof input === 'object') {
|
|
321
|
+
const patterns = Object.keys(input);
|
|
322
|
+
return (filePath) => {
|
|
323
|
+
for (const pattern of patterns) {
|
|
324
|
+
if (minimatch(filePath, pattern)) {
|
|
325
|
+
return input[pattern];
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
// false if not matched
|
|
329
|
+
};
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
// default
|
|
333
|
+
return () => true;
|
|
334
|
+
};
|
|
335
|
+
|
|
336
|
+
const resolveAllDataType = (type, dataList) => {
|
|
337
|
+
if (!type && dataList.length) {
|
|
338
|
+
const firstData = dataList[0];
|
|
339
|
+
type = firstData.type;
|
|
340
|
+
}
|
|
341
|
+
const types = {
|
|
342
|
+
v8: 'v8',
|
|
343
|
+
istanbul: 'istanbul'
|
|
344
|
+
};
|
|
345
|
+
return types[type] || types.v8;
|
|
346
|
+
};
|
|
291
347
|
|
|
292
|
-
const
|
|
348
|
+
const resolveAllOptions = (input, dataList) => {
|
|
349
|
+
if (!input) {
|
|
350
|
+
return;
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
let type;
|
|
354
|
+
let dir;
|
|
355
|
+
let filter;
|
|
356
|
+
if (typeof input === 'string' || Array.isArray(input)) {
|
|
357
|
+
dir = input;
|
|
358
|
+
filter = () => true;
|
|
359
|
+
} else {
|
|
360
|
+
type = input.type;
|
|
361
|
+
dir = input.dir;
|
|
362
|
+
filter = input.filter;
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
const dirList = resolveAllDirList(dir);
|
|
366
|
+
if (!dirList) {
|
|
367
|
+
return;
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
const dataType = resolveAllDataType(type, dataList);
|
|
371
|
+
const fileFilter = resolveAllFilter(filter);
|
|
372
|
+
|
|
373
|
+
return {
|
|
374
|
+
dirList,
|
|
375
|
+
dataType,
|
|
376
|
+
fileFilter
|
|
377
|
+
};
|
|
378
|
+
};
|
|
379
|
+
|
|
380
|
+
const resolveFileType = (fileType, filePath) => {
|
|
381
|
+
if (fileType === 'js' || fileType === 'css') {
|
|
382
|
+
return fileType;
|
|
383
|
+
}
|
|
384
|
+
const extname = path.extname(filePath);
|
|
385
|
+
if (['.css', '.scss', '.sass', '.less'].includes(extname)) {
|
|
386
|
+
return 'css';
|
|
387
|
+
}
|
|
388
|
+
return 'js';
|
|
389
|
+
};
|
|
390
|
+
|
|
391
|
+
const getV8EmptyData = (fileList) => {
|
|
392
|
+
const v8DataList = [];
|
|
393
|
+
fileList.forEach((item) => {
|
|
394
|
+
const { filePath, fileType } = item;
|
|
395
|
+
const type = resolveFileType(fileType, filePath);
|
|
396
|
+
const url = pathToFileURL(filePath).toString();
|
|
397
|
+
const source = Util.readFileSync(filePath);
|
|
398
|
+
const entryFile = {
|
|
399
|
+
empty: true,
|
|
400
|
+
type,
|
|
401
|
+
url,
|
|
402
|
+
// for empty, css supports both source and text
|
|
403
|
+
source
|
|
404
|
+
};
|
|
405
|
+
v8DataList.push(entryFile);
|
|
406
|
+
});
|
|
407
|
+
return v8DataList;
|
|
408
|
+
};
|
|
409
|
+
|
|
410
|
+
const getIstanbulEmptyData = (fileList) => {
|
|
411
|
+
const istanbulData = {};
|
|
412
|
+
fileList.forEach((item) => {
|
|
413
|
+
const { filePath } = item;
|
|
414
|
+
istanbulData[filePath] = {
|
|
415
|
+
path: filePath,
|
|
416
|
+
statementMap: {},
|
|
417
|
+
fnMap: {},
|
|
418
|
+
branchMap: {},
|
|
419
|
+
s: {},
|
|
420
|
+
f: {},
|
|
421
|
+
b: {}
|
|
422
|
+
};
|
|
423
|
+
});
|
|
424
|
+
return istanbulData;
|
|
425
|
+
};
|
|
426
|
+
|
|
427
|
+
const getEmptyData = (all, dataList) => {
|
|
428
|
+
const allOptions = resolveAllOptions(all, dataList);
|
|
429
|
+
// console.log('allOptions', all, allOptions);
|
|
430
|
+
if (!allOptions) {
|
|
431
|
+
return;
|
|
432
|
+
}
|
|
433
|
+
const {
|
|
434
|
+
dirList, dataType, fileFilter
|
|
435
|
+
} = allOptions;
|
|
436
|
+
const fileList = [];
|
|
437
|
+
dirList.forEach((dir) => {
|
|
438
|
+
Util.forEachFile(dir, [], (fileName, fileDir) => {
|
|
439
|
+
const filePath = path.resolve(fileDir, fileName);
|
|
440
|
+
const fileType = fileFilter(filePath);
|
|
441
|
+
if (fileType) {
|
|
442
|
+
fileList.push({
|
|
443
|
+
filePath,
|
|
444
|
+
fileType
|
|
445
|
+
});
|
|
446
|
+
}
|
|
447
|
+
});
|
|
448
|
+
});
|
|
449
|
+
// console.log('fileList', fileList);
|
|
450
|
+
if (!fileList.length) {
|
|
451
|
+
return;
|
|
452
|
+
}
|
|
453
|
+
if (dataType === 'v8') {
|
|
454
|
+
return getV8EmptyData(fileList);
|
|
455
|
+
}
|
|
456
|
+
return getIstanbulEmptyData(fileList);
|
|
457
|
+
};
|
|
458
|
+
|
|
459
|
+
// ========================================================================================================
|
|
460
|
+
|
|
461
|
+
|
|
462
|
+
const getInputData = async (inputList, all, cacheDir, addHandler) => {
|
|
293
463
|
const dataList = [];
|
|
294
464
|
const sourceCache = new Map();
|
|
295
465
|
|
|
@@ -321,6 +491,35 @@ const getInputData = async (inputList) => {
|
|
|
321
491
|
}
|
|
322
492
|
}
|
|
323
493
|
}
|
|
494
|
+
|
|
495
|
+
// add empty coverage for all files
|
|
496
|
+
// dataList for data type, before `add` we don't which one is tested, because using hash id
|
|
497
|
+
const emptyData = getEmptyData(all, dataList);
|
|
498
|
+
if (emptyData) {
|
|
499
|
+
const coverageResults = await addHandler(emptyData);
|
|
500
|
+
// after `add`
|
|
501
|
+
if (coverageResults) {
|
|
502
|
+
dataList.push(coverageResults);
|
|
503
|
+
if (coverageResults.type === 'v8') {
|
|
504
|
+
// sourceCache
|
|
505
|
+
coverageResults.data.forEach((entry) => {
|
|
506
|
+
const { id } = entry;
|
|
507
|
+
if (sourceCache.has(id)) {
|
|
508
|
+
// console.log('+', entry.sourcePath);
|
|
509
|
+
return;
|
|
510
|
+
}
|
|
511
|
+
// console.log('-', entry.sourcePath);
|
|
512
|
+
const sourcePath = Util.resolveCacheSourcePath(cacheDir, id);
|
|
513
|
+
const content = Util.readFileSync(sourcePath);
|
|
514
|
+
if (content) {
|
|
515
|
+
const json = JSON.parse(content);
|
|
516
|
+
sourceCache.set(json.id, json);
|
|
517
|
+
}
|
|
518
|
+
});
|
|
519
|
+
}
|
|
520
|
+
}
|
|
521
|
+
}
|
|
522
|
+
|
|
324
523
|
return {
|
|
325
524
|
dataList,
|
|
326
525
|
sourceCache
|
package/lib/index.d.ts
CHANGED
|
@@ -220,19 +220,41 @@ declare namespace MCR {
|
|
|
220
220
|
|
|
221
221
|
/** (V8 only)
|
|
222
222
|
*
|
|
223
|
-
* {string} `minimatch` pattern for entry url; multiple patterns
|
|
223
|
+
* {string} `minimatch` pattern for entry url; {object} multiple patterns;
|
|
224
224
|
*
|
|
225
225
|
* {function} A filter function for each entry file in the V8 list.
|
|
226
226
|
* */
|
|
227
|
-
entryFilter?: string |
|
|
227
|
+
entryFilter?: string | {
|
|
228
|
+
[pattern: string]: boolean;
|
|
229
|
+
} | ((entry: V8CoverageEntry) => boolean);
|
|
228
230
|
|
|
229
231
|
/** (V8 only)
|
|
230
232
|
*
|
|
231
|
-
* {string} `minimatch` pattern for source path; multiple patterns
|
|
233
|
+
* {string} `minimatch` pattern for source path; {object} multiple patterns;
|
|
232
234
|
*
|
|
233
235
|
* {function} A filter function for each source path when the source is unpacked from the source map.
|
|
234
236
|
* */
|
|
235
|
-
sourceFilter?: string |
|
|
237
|
+
sourceFilter?: string | {
|
|
238
|
+
[pattern: string]: boolean;
|
|
239
|
+
} | ((sourcePath: string) => boolean);
|
|
240
|
+
|
|
241
|
+
/**
|
|
242
|
+
* options for adding empty coverage for all files
|
|
243
|
+
*/
|
|
244
|
+
all?: string | string[] | {
|
|
245
|
+
/** the dir(s) of all files */
|
|
246
|
+
dir: string | string[];
|
|
247
|
+
/** the coverage data type, v8 or istanbul */
|
|
248
|
+
type?: "v8" | "istanbul";
|
|
249
|
+
/**
|
|
250
|
+
* the file filter, return the file type, if true, defaults to css if ".css" otherwise is js
|
|
251
|
+
*
|
|
252
|
+
* {string} `minimatch` pattern for file path; {object} multiple patterns; {function} A filter function for file path;
|
|
253
|
+
*/
|
|
254
|
+
filter?: string | {
|
|
255
|
+
[pattern: string]: "js" | "css" | boolean;
|
|
256
|
+
} | ((filePath: string) => "js" | "css" | boolean)
|
|
257
|
+
};
|
|
236
258
|
|
|
237
259
|
/** (V8 only) {boolean} Enable/Disable ignoring uncovered codes with the special comments: v8 ignore next/next N/start/stop */
|
|
238
260
|
v8Ignore?: boolean;
|
|
@@ -242,8 +264,14 @@ declare namespace MCR {
|
|
|
242
264
|
/** {boolean} Generate lcov.info file, same as lcovonly report. Defaults to false. */
|
|
243
265
|
lcov?: boolean;
|
|
244
266
|
|
|
245
|
-
/**
|
|
246
|
-
|
|
267
|
+
/**
|
|
268
|
+
* {function} Source path handler.
|
|
269
|
+
*
|
|
270
|
+
* {object} Replace key with value.
|
|
271
|
+
* */
|
|
272
|
+
sourcePath?: ((filePath: string) => string) | {
|
|
273
|
+
[key: string]: string;
|
|
274
|
+
};
|
|
247
275
|
|
|
248
276
|
/** {string|function} Specify the report path, especially when there are multiple reports. Defaults to outputDir/index.html. */
|
|
249
277
|
reportPath?: string | (() => string);
|
package/lib/index.js
CHANGED
|
@@ -83,7 +83,10 @@ class CoverageReport {
|
|
|
83
83
|
|
|
84
84
|
this.initOptions();
|
|
85
85
|
|
|
86
|
-
const {
|
|
86
|
+
const {
|
|
87
|
+
all, inputDir, cacheDir
|
|
88
|
+
} = this.options;
|
|
89
|
+
|
|
87
90
|
const inputDirs = Util.toList(inputDir, ',');
|
|
88
91
|
if (this.hasCache()) {
|
|
89
92
|
inputDirs.push(cacheDir);
|
|
@@ -92,16 +95,16 @@ class CoverageReport {
|
|
|
92
95
|
const inputList = inputDirs.filter((dir) => {
|
|
93
96
|
const hasDir = fs.existsSync(dir);
|
|
94
97
|
if (!hasDir) {
|
|
95
|
-
|
|
98
|
+
// could be empty
|
|
99
|
+
Util.logInfo(`Not found coverage data dir: ${Util.relativePath(dir)}`);
|
|
96
100
|
}
|
|
97
101
|
return hasDir;
|
|
98
102
|
});
|
|
99
103
|
|
|
100
|
-
|
|
101
|
-
return;
|
|
102
|
-
}
|
|
104
|
+
const { dataList, sourceCache } = await getInputData(inputList, all, cacheDir, (emptyData) => {
|
|
105
|
+
return this.add(emptyData);
|
|
106
|
+
});
|
|
103
107
|
|
|
104
|
-
const { dataList, sourceCache } = await getInputData(inputList);
|
|
105
108
|
if (!dataList.length) {
|
|
106
109
|
const dirs = inputList.map((dir) => Util.relativePath(dir));
|
|
107
110
|
Util.logError(`Not found coverage data in dir(s): ${dirs.join(', ')}`);
|
package/lib/istanbul/istanbul.js
CHANGED
|
@@ -7,7 +7,7 @@ const istanbulLibReport = require('istanbul-lib-report');
|
|
|
7
7
|
const Util = require('../utils/util.js');
|
|
8
8
|
const IstanbulSummary = require('./istanbul-summary.js');
|
|
9
9
|
|
|
10
|
-
const { initIstanbulSourcePath } = require('../utils/source-path.js');
|
|
10
|
+
const { getSourcePathHandler, initIstanbulSourcePath } = require('../utils/source-path.js');
|
|
11
11
|
|
|
12
12
|
const findHtmlPath = (outputDir) => {
|
|
13
13
|
const defaultHtml = path.resolve(outputDir, 'index.html');
|
|
@@ -129,7 +129,7 @@ const mergeIstanbulCoverage = (dataList) => {
|
|
|
129
129
|
const initIstanbulData = (istanbulData, options) => {
|
|
130
130
|
|
|
131
131
|
const fileSources = options.fileSources || {};
|
|
132
|
-
const sourcePathHandler = options
|
|
132
|
+
const sourcePathHandler = getSourcePathHandler(options);
|
|
133
133
|
|
|
134
134
|
const coverageData = initIstanbulSourcePath(istanbulData, fileSources, sourcePathHandler);
|
|
135
135
|
|