monocart-coverage-reports 2.10.4 → 2.10.6
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 +2 -0
- package/README.zh-Hans.md +2 -0
- package/lib/converter/converter.js +239 -129
- package/lib/converter/untested.js +237 -0
- package/lib/default/options.js +2 -1
- package/lib/generate.js +3 -238
- package/lib/index.d.ts +9 -2
- package/lib/istanbul/istanbul.js +27 -2
- package/lib/packages/monocart-coverage-vendor.js +2 -2
- package/lib/utils/util.js +19 -0
- package/lib/v8/v8.js +16 -36
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -967,6 +967,8 @@ process.env.MCR_LOG_TIME = true
|
|
|
967
967
|
- Coverage for Next.js, both server side and client side:
|
|
968
968
|
- [nextjs-with-playwright](https://github.com/cenfun/nextjs-with-playwright)
|
|
969
969
|
- [nextjs-with-playwright-istanbul](https://github.com/cenfun/nextjs-with-playwright-istanbul)
|
|
970
|
+
- Coverage for Remix:
|
|
971
|
+
- [remix-with-playwright](https://github.com/cenfun/remix-with-playwright)
|
|
970
972
|
- see [Collecting V8 Coverage Data with Playwright](#collecting-v8-coverage-data-with-playwright)
|
|
971
973
|
|
|
972
974
|
### [c8](https://github.com/bcoe/c8)
|
package/README.zh-Hans.md
CHANGED
|
@@ -971,6 +971,8 @@ process.env.MCR_LOG_TIME = true
|
|
|
971
971
|
- Coverage for Next.js, both server side and client side:
|
|
972
972
|
- [nextjs-with-playwright](https://github.com/cenfun/nextjs-with-playwright)
|
|
973
973
|
- [nextjs-with-playwright-istanbul](https://github.com/cenfun/nextjs-with-playwright-istanbul)
|
|
974
|
+
- Coverage for Remix:
|
|
975
|
+
- [remix-with-playwright](https://github.com/cenfun/remix-with-playwright)
|
|
974
976
|
- see [Collecting V8 Coverage Data with Playwright](#collecting-v8-coverage-data-with-playwright)
|
|
975
977
|
|
|
976
978
|
### [c8](https://github.com/bcoe/c8)
|
|
@@ -13,6 +13,8 @@ const findOriginalRange = require('./find-original-range.js');
|
|
|
13
13
|
const { getJsAstInfo, getCssAstInfo } = require('./ast.js');
|
|
14
14
|
const { getIgnoredRanges } = require('./ignore.js');
|
|
15
15
|
|
|
16
|
+
const { getUntestedList } = require('./untested.js');
|
|
17
|
+
|
|
16
18
|
const {
|
|
17
19
|
sortRanges, dedupeCountRanges, mergeRangesWith
|
|
18
20
|
} = require('../utils/dedupe.js');
|
|
@@ -1333,66 +1335,215 @@ const initJsCoverageList = (item) => {
|
|
|
1333
1335
|
return coverageList;
|
|
1334
1336
|
};
|
|
1335
1337
|
|
|
1336
|
-
|
|
1337
|
-
|
|
1338
|
-
|
|
1339
|
-
if (!uncoveredBytes.length) {
|
|
1340
|
-
return true;
|
|
1338
|
+
const logConvertTime = (msg, time_start, untested) => {
|
|
1339
|
+
if (untested) {
|
|
1340
|
+
return;
|
|
1341
1341
|
}
|
|
1342
|
-
|
|
1343
|
-
|
|
1344
|
-
|
|
1342
|
+
Util.logTime(msg, time_start);
|
|
1343
|
+
};
|
|
1344
|
+
|
|
1345
|
+
const convertCoverages = (list, options, untested) => {
|
|
1346
|
+
const stateList = [];
|
|
1347
|
+
|
|
1348
|
+
for (const item of list) {
|
|
1349
|
+
// console.log([item.id]);
|
|
1350
|
+
|
|
1351
|
+
const time_start_ast = Date.now();
|
|
1352
|
+
|
|
1353
|
+
const {
|
|
1354
|
+
type, source, fake, sourcePath
|
|
1355
|
+
} = item;
|
|
1356
|
+
|
|
1357
|
+
// for source file, type could be ts or vue as extname, but js = true
|
|
1358
|
+
const js = type === 'js';
|
|
1359
|
+
item.js = js;
|
|
1360
|
+
|
|
1361
|
+
// source mapping
|
|
1362
|
+
const locator = new Locator(source);
|
|
1363
|
+
const maxContentLength = source.length;
|
|
1364
|
+
|
|
1365
|
+
// ============================
|
|
1366
|
+
// move sourceMap
|
|
1367
|
+
const sourceMap = item.sourceMap;
|
|
1368
|
+
if (sourceMap) {
|
|
1369
|
+
delete item.sourceMap;
|
|
1370
|
+
}
|
|
1371
|
+
|
|
1372
|
+
// ============================
|
|
1373
|
+
// move functions and ranges to coverageList
|
|
1374
|
+
let coverageList = [];
|
|
1375
|
+
let astInfo;
|
|
1376
|
+
if (js) {
|
|
1377
|
+
coverageList = initJsCoverageList(item);
|
|
1378
|
+
// remove original functions
|
|
1379
|
+
if (!Util.isDebug()) {
|
|
1380
|
+
delete item.functions;
|
|
1381
|
+
}
|
|
1382
|
+
astInfo = getJsAstInfo(item, coverageList);
|
|
1383
|
+
} else {
|
|
1384
|
+
// convent css covered ranges to rules ranges and include uncovered ranges
|
|
1385
|
+
astInfo = getCssAstInfo(item, coverageList);
|
|
1386
|
+
// remove original ranges
|
|
1387
|
+
if (!Util.isDebug()) {
|
|
1388
|
+
delete item.ranges;
|
|
1389
|
+
}
|
|
1345
1390
|
}
|
|
1391
|
+
|
|
1392
|
+
logConvertTime(` ┌ [convert] parsed ast: ${sourcePath} (${EC.cyan(Util.BSF(maxContentLength))})`, time_start_ast, untested);
|
|
1393
|
+
|
|
1394
|
+
// console.log(sourcePath, astInfo.statements.length);
|
|
1395
|
+
// ============================
|
|
1396
|
+
|
|
1397
|
+
const time_start_unpack = Date.now();
|
|
1398
|
+
|
|
1399
|
+
// current file and it's sources from sourceMap
|
|
1400
|
+
// see const originalState
|
|
1401
|
+
const state = {
|
|
1402
|
+
js,
|
|
1403
|
+
type,
|
|
1404
|
+
source,
|
|
1405
|
+
fake,
|
|
1406
|
+
sourcePath,
|
|
1407
|
+
sourceMap,
|
|
1408
|
+
locator,
|
|
1409
|
+
maxContentLength,
|
|
1410
|
+
decodedMappings: [],
|
|
1411
|
+
rangeCache: new Map(),
|
|
1412
|
+
diffCache: new Map(),
|
|
1413
|
+
|
|
1414
|
+
// alignTextList: [],
|
|
1415
|
+
|
|
1416
|
+
// coverage info
|
|
1417
|
+
bytes: [],
|
|
1418
|
+
functions: [],
|
|
1419
|
+
branches: [],
|
|
1420
|
+
statements: [],
|
|
1421
|
+
astInfo,
|
|
1422
|
+
// for sub source files
|
|
1423
|
+
coverageList,
|
|
1424
|
+
originalList: [],
|
|
1425
|
+
// coverage data
|
|
1426
|
+
v8Data: item
|
|
1427
|
+
};
|
|
1428
|
+
|
|
1429
|
+
unpackDistFile(item, state, options);
|
|
1430
|
+
const unpackedFiles = EC.cyan(`${state.originalList.length} files`);
|
|
1431
|
+
|
|
1432
|
+
stateList.push(state);
|
|
1433
|
+
|
|
1434
|
+
logConvertTime(` ┌ [convert] unpacked sourcemap: ${sourcePath} (${unpackedFiles})`, time_start_unpack, untested);
|
|
1435
|
+
|
|
1346
1436
|
}
|
|
1347
|
-
|
|
1437
|
+
|
|
1438
|
+
return stateList;
|
|
1439
|
+
};
|
|
1440
|
+
|
|
1441
|
+
// ========================================================================================================
|
|
1442
|
+
|
|
1443
|
+
const isUncoveredRange = (range, key, uncoveredGroups) => {
|
|
1444
|
+
|
|
1445
|
+
let uncovered = true;
|
|
1446
|
+
uncoveredGroups.forEach((group) => {
|
|
1447
|
+
const { groupMap, state } = group;
|
|
1448
|
+
|
|
1449
|
+
// ========================================================
|
|
1450
|
+
// self key
|
|
1451
|
+
if (groupMap.has(key)) {
|
|
1452
|
+
return;
|
|
1453
|
+
}
|
|
1454
|
+
|
|
1455
|
+
// ========================================================
|
|
1456
|
+
// in all group
|
|
1457
|
+
for (const item of groupMap.values()) {
|
|
1458
|
+
if (range.start >= item.start && range.end <= item.end) {
|
|
1459
|
+
return;
|
|
1460
|
+
}
|
|
1461
|
+
}
|
|
1462
|
+
|
|
1463
|
+
// ========================================================
|
|
1464
|
+
// no sourcemap mappings in range
|
|
1465
|
+
// check original range in decodedMappings
|
|
1466
|
+
const { decodedMappings } = state;
|
|
1467
|
+
const item = decodedMappings.find((it) => it.originalOffset >= range.start && it.originalOffset <= range.end);
|
|
1468
|
+
if (!item) {
|
|
1469
|
+
return;
|
|
1470
|
+
}
|
|
1471
|
+
|
|
1472
|
+
// ========================================================
|
|
1473
|
+
uncovered = false;
|
|
1474
|
+
|
|
1475
|
+
});
|
|
1476
|
+
|
|
1477
|
+
return uncovered;
|
|
1478
|
+
|
|
1348
1479
|
};
|
|
1349
1480
|
|
|
1350
1481
|
const mergeV8Data = (state, stateList) => {
|
|
1351
1482
|
// console.log(stateList);
|
|
1352
1483
|
|
|
1353
|
-
//
|
|
1354
|
-
//
|
|
1355
|
-
//
|
|
1356
|
-
//
|
|
1357
|
-
//
|
|
1358
|
-
// console.log(stateList.map((it) => it.
|
|
1359
|
-
// console.log(stateList.map((it) => it.
|
|
1484
|
+
// let debug = false;
|
|
1485
|
+
// if (state.sourcePath.endsWith('counter.tsx')) {
|
|
1486
|
+
// debug = true;
|
|
1487
|
+
// console.log('merge v8 data ===================================', state.sourcePath);
|
|
1488
|
+
// console.log('bytes before ============', stateList.map((it) => it.bytes));
|
|
1489
|
+
// // console.log('statements ==============', stateList.map((it) => it.statements));
|
|
1490
|
+
// // console.log('functions ==============', stateList.map((it) => it.functions));
|
|
1491
|
+
// // console.log('branches ======================', stateList.map((it) => it.branches.map((b) => [`${b.start}-${b.end}`, JSON.stringify(b.locations.map((l) => l.count))])));
|
|
1360
1492
|
// }
|
|
1361
1493
|
|
|
1362
1494
|
// ===========================================================
|
|
1363
1495
|
// bytes
|
|
1364
1496
|
const mergedBytes = [];
|
|
1365
|
-
|
|
1497
|
+
|
|
1498
|
+
const coveredMap = new Map();
|
|
1499
|
+
const uncoveredMap = new Map();
|
|
1500
|
+
const uncoveredGroups = [];
|
|
1366
1501
|
stateList.forEach((st) => {
|
|
1502
|
+
const groupMap = new Map();
|
|
1367
1503
|
const bytes = dedupeCountRanges(st.bytes);
|
|
1368
|
-
const uncoveredBytes = [];
|
|
1369
1504
|
bytes.forEach((range) => {
|
|
1505
|
+
const key = `${range.start}_${range.end}`;
|
|
1370
1506
|
if (range.count) {
|
|
1371
1507
|
mergedBytes.push(range);
|
|
1508
|
+
coveredMap.set(key, true);
|
|
1372
1509
|
} else {
|
|
1373
|
-
|
|
1510
|
+
uncoveredMap.set(key, range);
|
|
1511
|
+
groupMap.set(key, range);
|
|
1374
1512
|
}
|
|
1375
1513
|
});
|
|
1376
|
-
|
|
1514
|
+
|
|
1515
|
+
uncoveredGroups.push({
|
|
1516
|
+
groupMap,
|
|
1517
|
+
state: st
|
|
1518
|
+
});
|
|
1377
1519
|
});
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
|
|
1384
|
-
|
|
1385
|
-
|
|
1386
|
-
|
|
1387
|
-
|
|
1388
|
-
|
|
1520
|
+
|
|
1521
|
+
// if (debug) {
|
|
1522
|
+
// console.log('coveredMap ============', coveredMap);
|
|
1523
|
+
// console.log('uncoveredMap ============', uncoveredMap);
|
|
1524
|
+
// console.log('uncoveredGroups ============', uncoveredGroups);
|
|
1525
|
+
// }
|
|
1526
|
+
|
|
1527
|
+
uncoveredMap.forEach((range, key) => {
|
|
1528
|
+
|
|
1529
|
+
// in covered range
|
|
1530
|
+
if (coveredMap.has(key)) {
|
|
1531
|
+
return;
|
|
1532
|
+
}
|
|
1533
|
+
|
|
1534
|
+
if (isUncoveredRange(range, key, uncoveredGroups)) {
|
|
1389
1535
|
mergedBytes.push(range);
|
|
1390
|
-
}
|
|
1536
|
+
}
|
|
1537
|
+
|
|
1391
1538
|
});
|
|
1392
1539
|
|
|
1393
1540
|
// will be dedupeCountRanges in collectFileCoverage
|
|
1394
1541
|
state.bytes = mergedBytes;
|
|
1395
1542
|
|
|
1543
|
+
// if (debug) {
|
|
1544
|
+
// console.log('bytes after ============', mergedBytes);
|
|
1545
|
+
// }
|
|
1546
|
+
|
|
1396
1547
|
// ===========================================================
|
|
1397
1548
|
// functions
|
|
1398
1549
|
const allFunctions = stateList.map((it) => it.functions).flat();
|
|
@@ -1432,6 +1583,20 @@ const mergeV8Data = (state, stateList) => {
|
|
|
1432
1583
|
const mergedFunctions = mergeRangesWith(allFunctions, functionComparer, functionHandler);
|
|
1433
1584
|
state.functions = mergedFunctions;
|
|
1434
1585
|
|
|
1586
|
+
// ===========================================================
|
|
1587
|
+
// statements
|
|
1588
|
+
const allStatements = stateList.map((it) => it.statements).flat();
|
|
1589
|
+
const statementComparer = (lastRange, range) => {
|
|
1590
|
+
// exact matched because the statement range is generated from ast
|
|
1591
|
+
return lastRange.start === range.start && lastRange.end === range.end;
|
|
1592
|
+
};
|
|
1593
|
+
const statementHandler = (lastRange, range) => {
|
|
1594
|
+
// merge statements count
|
|
1595
|
+
lastRange.count += range.count;
|
|
1596
|
+
};
|
|
1597
|
+
const mergedStatements = mergeRangesWith(allStatements, statementComparer, statementHandler);
|
|
1598
|
+
state.statements = mergedStatements;
|
|
1599
|
+
|
|
1435
1600
|
// ===========================================================
|
|
1436
1601
|
// branches
|
|
1437
1602
|
const allBranches = stateList.map((it) => it.branches).flat();
|
|
@@ -1458,6 +1623,43 @@ const mergeV8Data = (state, stateList) => {
|
|
|
1458
1623
|
// }
|
|
1459
1624
|
|
|
1460
1625
|
|
|
1626
|
+
};
|
|
1627
|
+
|
|
1628
|
+
const addUntestedFiles = async (stateList, options) => {
|
|
1629
|
+
|
|
1630
|
+
const time_start_untested = Date.now();
|
|
1631
|
+
|
|
1632
|
+
const testedMap = new Map();
|
|
1633
|
+
stateList.forEach((state) => {
|
|
1634
|
+
const { v8Data, originalList } = state;
|
|
1635
|
+
// dedupe dist file if not debug
|
|
1636
|
+
if (!v8Data.dedupe) {
|
|
1637
|
+
testedMap.set(state.sourcePath, true);
|
|
1638
|
+
}
|
|
1639
|
+
originalList.forEach((originalState) => {
|
|
1640
|
+
testedMap.set(originalState.sourcePath, true);
|
|
1641
|
+
});
|
|
1642
|
+
});
|
|
1643
|
+
|
|
1644
|
+
// console.log('testedMap', testedMap);
|
|
1645
|
+
|
|
1646
|
+
const untestedList = await getUntestedList(testedMap, options, 'v8');
|
|
1647
|
+
if (!untestedList) {
|
|
1648
|
+
return;
|
|
1649
|
+
}
|
|
1650
|
+
|
|
1651
|
+
const untestedStateList = convertCoverages(untestedList, options, true);
|
|
1652
|
+
|
|
1653
|
+
// console.log(untestedStateList);
|
|
1654
|
+
untestedStateList.forEach((state) => {
|
|
1655
|
+
stateList.push(state);
|
|
1656
|
+
});
|
|
1657
|
+
|
|
1658
|
+
// console.log('untestedList', untestedList);
|
|
1659
|
+
|
|
1660
|
+
Util.logTime(` ┌ [convert] added untested files: ${EC.yellow(untestedList.length)}`, time_start_untested);
|
|
1661
|
+
|
|
1662
|
+
|
|
1461
1663
|
};
|
|
1462
1664
|
|
|
1463
1665
|
const generateV8DataList = (stateList, options) => {
|
|
@@ -1536,109 +1738,17 @@ const generateV8DataList = (stateList, options) => {
|
|
|
1536
1738
|
};
|
|
1537
1739
|
};
|
|
1538
1740
|
|
|
1539
|
-
const convertV8List = (v8list, options) => {
|
|
1540
|
-
|
|
1541
|
-
const stateList = [];
|
|
1542
|
-
|
|
1543
|
-
for (const item of v8list) {
|
|
1544
|
-
// console.log([item.id]);
|
|
1545
|
-
|
|
1546
|
-
const time_start_ast = Date.now();
|
|
1547
|
-
|
|
1548
|
-
const {
|
|
1549
|
-
type, source, fake, sourcePath
|
|
1550
|
-
} = item;
|
|
1551
|
-
|
|
1552
|
-
// for source file, type could be ts or vue as extname, but js = true
|
|
1553
|
-
const js = type === 'js';
|
|
1554
|
-
item.js = js;
|
|
1741
|
+
const convertV8List = async (v8list, options) => {
|
|
1555
1742
|
|
|
1556
|
-
|
|
1557
|
-
|
|
1558
|
-
const maxContentLength = source.length;
|
|
1743
|
+
// for tested files
|
|
1744
|
+
const stateList = convertCoverages(v8list, options);
|
|
1559
1745
|
|
|
1560
|
-
|
|
1561
|
-
|
|
1562
|
-
const sourceMap = item.sourceMap;
|
|
1563
|
-
if (sourceMap) {
|
|
1564
|
-
delete item.sourceMap;
|
|
1565
|
-
}
|
|
1566
|
-
|
|
1567
|
-
// ============================
|
|
1568
|
-
// move functions and ranges to coverageList
|
|
1569
|
-
let coverageList = [];
|
|
1570
|
-
let astInfo;
|
|
1571
|
-
if (js) {
|
|
1572
|
-
coverageList = initJsCoverageList(item);
|
|
1573
|
-
// remove original functions
|
|
1574
|
-
if (!Util.isDebug()) {
|
|
1575
|
-
delete item.functions;
|
|
1576
|
-
}
|
|
1577
|
-
astInfo = getJsAstInfo(item, coverageList);
|
|
1578
|
-
} else {
|
|
1579
|
-
// convent css covered ranges to rules ranges and include uncovered ranges
|
|
1580
|
-
astInfo = getCssAstInfo(item, coverageList);
|
|
1581
|
-
// remove original ranges
|
|
1582
|
-
if (!Util.isDebug()) {
|
|
1583
|
-
delete item.ranges;
|
|
1584
|
-
}
|
|
1585
|
-
}
|
|
1586
|
-
|
|
1587
|
-
Util.logTime(` ┌ [convert] parsed ast: ${sourcePath} (${EC.cyan(Util.BSF(maxContentLength))})`, time_start_ast);
|
|
1588
|
-
|
|
1589
|
-
// console.log(sourcePath, astInfo.statements.length);
|
|
1590
|
-
// ============================
|
|
1591
|
-
|
|
1592
|
-
const time_start_unpack = Date.now();
|
|
1593
|
-
|
|
1594
|
-
// current file and it's sources from sourceMap
|
|
1595
|
-
// see const originalState
|
|
1596
|
-
const state = {
|
|
1597
|
-
js,
|
|
1598
|
-
type,
|
|
1599
|
-
source,
|
|
1600
|
-
fake,
|
|
1601
|
-
sourcePath,
|
|
1602
|
-
sourceMap,
|
|
1603
|
-
locator,
|
|
1604
|
-
maxContentLength,
|
|
1605
|
-
decodedMappings: [],
|
|
1606
|
-
rangeCache: new Map(),
|
|
1607
|
-
diffCache: new Map(),
|
|
1608
|
-
|
|
1609
|
-
// alignTextList: [],
|
|
1610
|
-
|
|
1611
|
-
// coverage info
|
|
1612
|
-
bytes: [],
|
|
1613
|
-
functions: [],
|
|
1614
|
-
branches: [],
|
|
1615
|
-
statements: [],
|
|
1616
|
-
astInfo,
|
|
1617
|
-
// for sub source files
|
|
1618
|
-
coverageList,
|
|
1619
|
-
originalList: [],
|
|
1620
|
-
// coverage data
|
|
1621
|
-
v8Data: item
|
|
1622
|
-
};
|
|
1623
|
-
|
|
1624
|
-
unpackDistFile(item, state, options);
|
|
1625
|
-
const unpackedFiles = EC.cyan(`${state.originalList.length} files`);
|
|
1626
|
-
|
|
1627
|
-
stateList.push(state);
|
|
1628
|
-
|
|
1629
|
-
Util.logTime(` ┌ [convert] unpacked sourcemap: ${sourcePath} (${unpackedFiles})`, time_start_unpack);
|
|
1630
|
-
|
|
1631
|
-
}
|
|
1746
|
+
// empty coverage handler
|
|
1747
|
+
await addUntestedFiles(stateList, options);
|
|
1632
1748
|
|
|
1633
1749
|
const time_start_convert = Date.now();
|
|
1634
|
-
|
|
1635
1750
|
const dataList = generateV8DataList(stateList, options);
|
|
1636
1751
|
const dataFiles = EC.cyan(`${dataList.v8DataList.length} files`);
|
|
1637
|
-
|
|
1638
|
-
// stateList.forEach((st, i) => {
|
|
1639
|
-
// Util.writeFileSync(`align-text-${i}.js`, `window.gridData = ${JSON.stringify(st.alignTextList, null, 4)}`);
|
|
1640
|
-
// });
|
|
1641
|
-
|
|
1642
1752
|
Util.logTime(` ┌ [convert] converted data list (${dataFiles})`, time_start_convert);
|
|
1643
1753
|
|
|
1644
1754
|
return dataList;
|
|
@@ -0,0 +1,237 @@
|
|
|
1
|
+
const fs = require('fs');
|
|
2
|
+
const path = require('path');
|
|
3
|
+
const Util = require('../utils/util.js');
|
|
4
|
+
|
|
5
|
+
const { pathToFileURL, fileURLToPath } = require('url');
|
|
6
|
+
|
|
7
|
+
// const EC = require('eight-colors');
|
|
8
|
+
const { minimatch } = require('../packages/monocart-coverage-vendor.js');
|
|
9
|
+
|
|
10
|
+
// ========================================================================================================
|
|
11
|
+
|
|
12
|
+
const resolveAllDirList = (input) => {
|
|
13
|
+
const dirs = Util.toList(input, ',');
|
|
14
|
+
if (!dirs.length) {
|
|
15
|
+
return;
|
|
16
|
+
}
|
|
17
|
+
const dirList = dirs.filter((it) => fs.existsSync(it));
|
|
18
|
+
if (!dirList.length) {
|
|
19
|
+
return;
|
|
20
|
+
}
|
|
21
|
+
return dirList;
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
const resolveAllFilter = (input) => {
|
|
25
|
+
// for function handler
|
|
26
|
+
if (typeof input === 'function') {
|
|
27
|
+
return input;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// for single minimatch pattern
|
|
31
|
+
if (input && typeof input === 'string') {
|
|
32
|
+
// string to multiple patterns "{...}"
|
|
33
|
+
const obj = Util.strToObj(input);
|
|
34
|
+
if (obj) {
|
|
35
|
+
input = obj;
|
|
36
|
+
} else {
|
|
37
|
+
return (filePath) => {
|
|
38
|
+
return minimatch(filePath, input);
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// for patterns
|
|
44
|
+
if (input && typeof input === 'object') {
|
|
45
|
+
const patterns = Object.keys(input);
|
|
46
|
+
return (filePath) => {
|
|
47
|
+
for (const pattern of patterns) {
|
|
48
|
+
if (minimatch(filePath, pattern)) {
|
|
49
|
+
return input[pattern];
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
// false if not matched
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// default
|
|
57
|
+
return () => true;
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
const resolveAllOptions = (input) => {
|
|
61
|
+
if (!input) {
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
let dir;
|
|
66
|
+
let filter;
|
|
67
|
+
let transformer;
|
|
68
|
+
|
|
69
|
+
if (typeof input === 'string') {
|
|
70
|
+
const obj = Util.strToObj(input);
|
|
71
|
+
if (obj) {
|
|
72
|
+
dir = obj.dir;
|
|
73
|
+
filter = obj.filter;
|
|
74
|
+
transformer = obj.transformer;
|
|
75
|
+
} else {
|
|
76
|
+
dir = input;
|
|
77
|
+
}
|
|
78
|
+
} else if (Array.isArray(input)) {
|
|
79
|
+
dir = input;
|
|
80
|
+
} else {
|
|
81
|
+
dir = input.dir;
|
|
82
|
+
filter = input.filter;
|
|
83
|
+
transformer = input.transformer;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
const dirList = resolveAllDirList(dir);
|
|
87
|
+
if (!dirList) {
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
const fileFilter = resolveAllFilter(filter);
|
|
92
|
+
const fileTransformer = typeof transformer === 'function' ? transformer : () => {};
|
|
93
|
+
|
|
94
|
+
return {
|
|
95
|
+
dirList,
|
|
96
|
+
fileFilter,
|
|
97
|
+
fileTransformer
|
|
98
|
+
};
|
|
99
|
+
};
|
|
100
|
+
|
|
101
|
+
const resolveFileType = (fileType, filePath) => {
|
|
102
|
+
if (fileType === 'js' || fileType === 'css') {
|
|
103
|
+
return fileType;
|
|
104
|
+
}
|
|
105
|
+
const extname = path.extname(filePath);
|
|
106
|
+
if (['.css', '.scss', '.sass', '.less'].includes(extname)) {
|
|
107
|
+
return 'css';
|
|
108
|
+
}
|
|
109
|
+
return 'js';
|
|
110
|
+
};
|
|
111
|
+
|
|
112
|
+
const getUntestedCoverageData = (emptyList, options, coverageType) => {
|
|
113
|
+
|
|
114
|
+
// NOTE: do not save untested file to raw report
|
|
115
|
+
|
|
116
|
+
const emptyCoverageList = [];
|
|
117
|
+
|
|
118
|
+
// save all empty source and sourcemap
|
|
119
|
+
for (const entryFile of emptyList) {
|
|
120
|
+
|
|
121
|
+
const { type, url } = entryFile;
|
|
122
|
+
|
|
123
|
+
if (coverageType === 'istanbul') {
|
|
124
|
+
|
|
125
|
+
// ===============================================
|
|
126
|
+
const item = {
|
|
127
|
+
path: fileURLToPath(url),
|
|
128
|
+
statementMap: {},
|
|
129
|
+
fnMap: {},
|
|
130
|
+
branchMap: {},
|
|
131
|
+
s: {},
|
|
132
|
+
f: {},
|
|
133
|
+
b: {}
|
|
134
|
+
};
|
|
135
|
+
// object
|
|
136
|
+
emptyCoverageList.push(item);
|
|
137
|
+
|
|
138
|
+
} else {
|
|
139
|
+
|
|
140
|
+
// ===============================================
|
|
141
|
+
if (type === 'js') {
|
|
142
|
+
// empty js
|
|
143
|
+
entryFile.functions = entryFile.functions || [{
|
|
144
|
+
functionName: '',
|
|
145
|
+
ranges: [{
|
|
146
|
+
startOffset: 0,
|
|
147
|
+
endOffset: entryFile.source.length,
|
|
148
|
+
count: 0
|
|
149
|
+
}]
|
|
150
|
+
}];
|
|
151
|
+
} else {
|
|
152
|
+
// empty css
|
|
153
|
+
entryFile.ranges = entryFile.ranges || [];
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
// will be parsed to AST and converted to V8 coverage
|
|
157
|
+
emptyCoverageList.push(entryFile);
|
|
158
|
+
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
return emptyCoverageList;
|
|
164
|
+
};
|
|
165
|
+
|
|
166
|
+
const getEmptyCoverages = async (fileList, options, coverageType, fileTransformer) => {
|
|
167
|
+
|
|
168
|
+
const emptyList = [];
|
|
169
|
+
|
|
170
|
+
for (const item of fileList) {
|
|
171
|
+
const {
|
|
172
|
+
filePath, fileType, sourcePath
|
|
173
|
+
} = item;
|
|
174
|
+
const type = resolveFileType(fileType, filePath);
|
|
175
|
+
const url = pathToFileURL(filePath).toString();
|
|
176
|
+
const source = Util.readFileSync(filePath);
|
|
177
|
+
const entryFile = {
|
|
178
|
+
empty: true,
|
|
179
|
+
type,
|
|
180
|
+
url,
|
|
181
|
+
sourcePath,
|
|
182
|
+
// for empty, css supports both source and text
|
|
183
|
+
source
|
|
184
|
+
};
|
|
185
|
+
|
|
186
|
+
await fileTransformer(entryFile, coverageType);
|
|
187
|
+
|
|
188
|
+
// after transformer
|
|
189
|
+
entryFile.id = Util.calculateSha1(entryFile.sourcePath + entryFile.source);
|
|
190
|
+
|
|
191
|
+
emptyList.push(entryFile);
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
// save empty coverage for merging raw reports
|
|
195
|
+
return getUntestedCoverageData(emptyList, options, coverageType);
|
|
196
|
+
};
|
|
197
|
+
|
|
198
|
+
const getUntestedList = (testedMap, options, coverageType = 'v8') => {
|
|
199
|
+
const allOptions = resolveAllOptions(options.all);
|
|
200
|
+
// console.log('allOptions', all, allOptions);
|
|
201
|
+
if (!allOptions) {
|
|
202
|
+
return;
|
|
203
|
+
}
|
|
204
|
+
const {
|
|
205
|
+
dirList, fileFilter, fileTransformer
|
|
206
|
+
} = allOptions;
|
|
207
|
+
const fileList = [];
|
|
208
|
+
dirList.forEach((dir) => {
|
|
209
|
+
Util.forEachFile(dir, [], (fileName, fileDir) => {
|
|
210
|
+
const filePath = path.resolve(fileDir, fileName);
|
|
211
|
+
// return file extname for file type
|
|
212
|
+
const fileType = fileFilter(filePath);
|
|
213
|
+
if (fileType) {
|
|
214
|
+
const sourcePath = Util.relativePath(filePath);
|
|
215
|
+
if (!testedMap.has(sourcePath)) {
|
|
216
|
+
fileList.push({
|
|
217
|
+
filePath,
|
|
218
|
+
fileType,
|
|
219
|
+
sourcePath
|
|
220
|
+
});
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
});
|
|
224
|
+
});
|
|
225
|
+
// console.log('fileList', fileList);
|
|
226
|
+
if (!fileList.length) {
|
|
227
|
+
return;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
return getEmptyCoverages(fileList, options, coverageType, fileTransformer);
|
|
231
|
+
|
|
232
|
+
};
|
|
233
|
+
|
|
234
|
+
|
|
235
|
+
module.exports = {
|
|
236
|
+
getUntestedList
|
|
237
|
+
};
|
package/lib/default/options.js
CHANGED