monocart-coverage-reports 2.3.4 → 2.4.0
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 +3 -1
- package/lib/converter/ast-visitor.js +646 -0
- package/lib/converter/ast.js +115 -135
- package/lib/converter/collect-source-maps.js +1 -1
- package/lib/converter/converter.js +288 -197
- package/lib/converter/find-original-range.js +471 -411
- package/lib/converter/info-branch.js +13 -2
- package/lib/converter/info-function.js +29 -6
- package/lib/converter/info-statement.js +43 -0
- package/lib/default/options.js +1 -1
- package/lib/generate.js +2 -2
- package/lib/index.d.ts +24 -13
- package/lib/istanbul/istanbul.js +1 -1
- package/lib/packages/monocart-coverage-v8.js +1 -1
- package/lib/packages/monocart-coverage-vendor.js +38 -37
- package/lib/utils/util.js +8 -2
- package/lib/v8/v8-summary.js +5 -1
- package/lib/v8/v8.js +4 -1
- package/package.json +3 -3
- package/lib/converter/branches.js +0 -440
- package/lib/converter/info-line.js +0 -23
package/README.md
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
[](https://www.npmjs.com/package/monocart-coverage-reports)
|
|
4
4
|
[](https://www.npmjs.com/package/monocart-coverage-reports)
|
|
5
|
+

|
|
5
6
|

|
|
6
7
|

|
|
7
8
|
|
|
@@ -405,10 +406,11 @@ console.log(coverageResults.summary);
|
|
|
405
406
|
|
|
406
407
|
## Merge Coverage Reports
|
|
407
408
|
The following usage scenarios may require merging coverage reports:
|
|
408
|
-
- When the code is executed in different environments, like Node.js Server Side and browser Client Side (Next.js for instance). Each environment may generate its own coverage report. Merging them can give a more comprehensive view of the test coverage.
|
|
409
|
+
- When the code is executed in different environments, like Node.js Server Side and browser Client Side (Next.js for instance). Each environment may generate its own coverage report. Merging them can give a more comprehensive view of the test coverage. see example [nextjs-with-playwright](https://github.com/cenfun/nextjs-with-playwright) for automatic report merging.
|
|
409
410
|
- When the code is subjected to different kinds of testing. For example, unit tests with Jest might cover certain parts of the code, while end-to-end tests with Playwright might cover other parts. Merging these different coverage reports can provide a holistic view of what code has been tested.
|
|
410
411
|
- When tests are run on different machines or different shards, each might produce its own coverage report. Merging these can give a complete picture of the test coverage across all machines or shards.
|
|
411
412
|
|
|
413
|
+
If the reports cannot be merged automatically, then here is how to manually merge the reports.
|
|
412
414
|
First, using the `raw` report to export the original coverage data to the specified directory.
|
|
413
415
|
```js
|
|
414
416
|
const coverageOptions = {
|
|
@@ -0,0 +1,646 @@
|
|
|
1
|
+
const Util = require('../utils/util.js');
|
|
2
|
+
|
|
3
|
+
const getParentFunctionState = (reverseParents) => {
|
|
4
|
+
const parentFunction = reverseParents.find((it) => it._state && it._state.isFunction);
|
|
5
|
+
if (!parentFunction) {
|
|
6
|
+
return;
|
|
7
|
+
}
|
|
8
|
+
return parentFunction._state;
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
const getParentCount = (reverseParents, functionCount) => {
|
|
12
|
+
// parent count
|
|
13
|
+
const parent = reverseParents.find((it) => it._state);
|
|
14
|
+
if (parent) {
|
|
15
|
+
return parent._state.count;
|
|
16
|
+
}
|
|
17
|
+
// root function count
|
|
18
|
+
return functionCount;
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
const getFunctionRange = (start, end, coverageInfo) => {
|
|
22
|
+
|
|
23
|
+
const {
|
|
24
|
+
functionMap, functionNameMap, functionUncoveredRanges
|
|
25
|
+
} = coverageInfo;
|
|
26
|
+
|
|
27
|
+
// exact matched in functionMap
|
|
28
|
+
const range = functionMap.get(start);
|
|
29
|
+
if (range) {
|
|
30
|
+
return range;
|
|
31
|
+
}
|
|
32
|
+
// exact matched in functionNameMap
|
|
33
|
+
const nameRange = functionNameMap.get(start);
|
|
34
|
+
if (nameRange) {
|
|
35
|
+
return nameRange;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// find in uncoveredRanges
|
|
39
|
+
return Util.findInRanges(start, end, functionUncoveredRanges, 'startOffset', 'endOffset');
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
const getFunctionBlock = (start, end, functionState) => {
|
|
43
|
+
if (!functionState) {
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const { range } = functionState;
|
|
48
|
+
if (!range) {
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
const {
|
|
53
|
+
blockMap, blockUncoveredRanges, blockCoveredRanges
|
|
54
|
+
} = range;
|
|
55
|
+
|
|
56
|
+
if (!blockMap) {
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
const block = blockMap.get(start);
|
|
61
|
+
if (block) {
|
|
62
|
+
return block;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
const uncoveredBlock = Util.findInRanges(start, end, blockUncoveredRanges, 'startOffset', 'endOffset');
|
|
66
|
+
if (uncoveredBlock) {
|
|
67
|
+
return uncoveredBlock;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// the block is not exact correct, if there is a block wrapped
|
|
71
|
+
// [x8]{ var a = b || [x4]c }, b is no block, but it can be found in x8 block
|
|
72
|
+
const coveredBlocks = blockCoveredRanges.filter((it) => start >= it.startOffset && end <= it.endOffset);
|
|
73
|
+
if (coveredBlocks.length) {
|
|
74
|
+
return coveredBlocks.pop();
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
const addNodeCount = (item) => {
|
|
80
|
+
|
|
81
|
+
const { node, reverseParents } = item;
|
|
82
|
+
|
|
83
|
+
if (node._state) {
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
const { start, end } = node;
|
|
88
|
+
const functionState = getParentFunctionState(reverseParents);
|
|
89
|
+
const block = getFunctionBlock(start, end, functionState);
|
|
90
|
+
if (block) {
|
|
91
|
+
node._state = {
|
|
92
|
+
count: block.count
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
};
|
|
97
|
+
|
|
98
|
+
// =======================================================================================
|
|
99
|
+
|
|
100
|
+
const createBranchGroup = (type, node, parents, branchMap) => {
|
|
101
|
+
const { start, end } = node;
|
|
102
|
+
// clone and reverse parents
|
|
103
|
+
const reverseParents = [].concat(parents).reverse();
|
|
104
|
+
const group = {
|
|
105
|
+
type,
|
|
106
|
+
|
|
107
|
+
start,
|
|
108
|
+
// could be updated if multiple locations
|
|
109
|
+
end,
|
|
110
|
+
|
|
111
|
+
locations: [],
|
|
112
|
+
reverseParents
|
|
113
|
+
};
|
|
114
|
+
|
|
115
|
+
if (type === 'LogicalExpression') {
|
|
116
|
+
// && or ||
|
|
117
|
+
group.operator = node.operator;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
branchMap.set(start, group);
|
|
121
|
+
return group;
|
|
122
|
+
};
|
|
123
|
+
|
|
124
|
+
const addBranch = (group, node, locationMap) => {
|
|
125
|
+
const { start, end } = node;
|
|
126
|
+
|
|
127
|
+
const branchStart = group.start;
|
|
128
|
+
|
|
129
|
+
const branchInfo = {
|
|
130
|
+
// for get previous group for LogicalExpression
|
|
131
|
+
branchStart,
|
|
132
|
+
start,
|
|
133
|
+
end,
|
|
134
|
+
// branch count default to 0
|
|
135
|
+
count: 0
|
|
136
|
+
};
|
|
137
|
+
|
|
138
|
+
group.locations.push(branchInfo);
|
|
139
|
+
|
|
140
|
+
// update group end
|
|
141
|
+
if (end > group.end) {
|
|
142
|
+
group.end = end;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
locationMap.set(start, branchInfo);
|
|
146
|
+
|
|
147
|
+
return branchInfo;
|
|
148
|
+
};
|
|
149
|
+
|
|
150
|
+
const addNoneBranch = (group) => {
|
|
151
|
+
group.locations.push({
|
|
152
|
+
none: true,
|
|
153
|
+
count: 0
|
|
154
|
+
});
|
|
155
|
+
};
|
|
156
|
+
|
|
157
|
+
// =======================================================================================
|
|
158
|
+
|
|
159
|
+
const updateBlockLocations = (locations) => {
|
|
160
|
+
const noBlockList = [];
|
|
161
|
+
let blockCount = 0;
|
|
162
|
+
locations.forEach((item) => {
|
|
163
|
+
if (item.block) {
|
|
164
|
+
item.count = item.block.count;
|
|
165
|
+
blockCount += item.count;
|
|
166
|
+
return;
|
|
167
|
+
}
|
|
168
|
+
noBlockList.push(item);
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
return {
|
|
172
|
+
noBlockList,
|
|
173
|
+
blockCount
|
|
174
|
+
};
|
|
175
|
+
};
|
|
176
|
+
|
|
177
|
+
// const a = tf1 ? 'true' : 'false';
|
|
178
|
+
const ConditionalExpression = (group, parentCount) => {
|
|
179
|
+
const { noBlockList, blockCount } = updateBlockLocations(group.locations);
|
|
180
|
+
if (!noBlockList.length) {
|
|
181
|
+
return;
|
|
182
|
+
}
|
|
183
|
+
let count = parentCount - blockCount;
|
|
184
|
+
noBlockList.forEach((item) => {
|
|
185
|
+
item.count = count;
|
|
186
|
+
count = 0;
|
|
187
|
+
});
|
|
188
|
+
};
|
|
189
|
+
|
|
190
|
+
const IfStatement = (group, parentCount) => {
|
|
191
|
+
const { noBlockList, blockCount } = updateBlockLocations(group.locations);
|
|
192
|
+
if (!noBlockList.length) {
|
|
193
|
+
return;
|
|
194
|
+
}
|
|
195
|
+
// console.log(parentCount, 'uncovered list', noBlockList.length, group.start);
|
|
196
|
+
let count = parentCount - blockCount;
|
|
197
|
+
noBlockList.forEach((item) => {
|
|
198
|
+
item.count = count;
|
|
199
|
+
count = 0;
|
|
200
|
+
});
|
|
201
|
+
|
|
202
|
+
};
|
|
203
|
+
|
|
204
|
+
// const b = tf2 || tf1 || a;
|
|
205
|
+
const LogicalExpression = (group, parentCount) => {
|
|
206
|
+
const { noBlockList, blockCount } = updateBlockLocations(group.locations);
|
|
207
|
+
if (!noBlockList.length) {
|
|
208
|
+
return;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
if (group.operator === '&&') {
|
|
212
|
+
// console.log(group.start, noBlockList);
|
|
213
|
+
noBlockList.forEach((item) => {
|
|
214
|
+
item.count = parentCount;
|
|
215
|
+
});
|
|
216
|
+
return;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
const count = parentCount - blockCount;
|
|
220
|
+
noBlockList.forEach((item) => {
|
|
221
|
+
item.count = count;
|
|
222
|
+
});
|
|
223
|
+
};
|
|
224
|
+
|
|
225
|
+
const SwitchStatement = (group, parentCount) => {
|
|
226
|
+
const { noBlockList, blockCount } = updateBlockLocations(group.locations);
|
|
227
|
+
if (!noBlockList.length) {
|
|
228
|
+
return;
|
|
229
|
+
}
|
|
230
|
+
const count = parentCount - blockCount;
|
|
231
|
+
noBlockList.forEach((item) => {
|
|
232
|
+
item.count = count;
|
|
233
|
+
});
|
|
234
|
+
};
|
|
235
|
+
|
|
236
|
+
const AssignmentPattern = (group, parentCount) => {
|
|
237
|
+
group.locations.forEach((item) => {
|
|
238
|
+
item.count = parentCount;
|
|
239
|
+
});
|
|
240
|
+
};
|
|
241
|
+
|
|
242
|
+
// =======================================================================================
|
|
243
|
+
|
|
244
|
+
const updateBranchCount = (group) => {
|
|
245
|
+
|
|
246
|
+
const {
|
|
247
|
+
type, locations, reverseParents
|
|
248
|
+
} = group;
|
|
249
|
+
|
|
250
|
+
const functionState = getParentFunctionState(reverseParents);
|
|
251
|
+
|
|
252
|
+
const functionCount = functionState.count;
|
|
253
|
+
|
|
254
|
+
// default is 0, no need continue
|
|
255
|
+
if (functionCount === 0) {
|
|
256
|
+
return;
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
// parent is block statement or function
|
|
260
|
+
let parentCount = getParentCount(reverseParents, functionCount);
|
|
261
|
+
// parent is group range
|
|
262
|
+
const groupBlock = getFunctionBlock(group.start, group.end, functionState);
|
|
263
|
+
if (groupBlock) {
|
|
264
|
+
parentCount = groupBlock.count;
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
// calculate branches count
|
|
268
|
+
locations.forEach((item) => {
|
|
269
|
+
const {
|
|
270
|
+
start, end, none
|
|
271
|
+
} = item;
|
|
272
|
+
if (none) {
|
|
273
|
+
return;
|
|
274
|
+
}
|
|
275
|
+
item.block = getFunctionBlock(start, end, functionState);
|
|
276
|
+
});
|
|
277
|
+
|
|
278
|
+
|
|
279
|
+
const handlers = {
|
|
280
|
+
ConditionalExpression,
|
|
281
|
+
IfStatement,
|
|
282
|
+
LogicalExpression,
|
|
283
|
+
SwitchStatement,
|
|
284
|
+
AssignmentPattern
|
|
285
|
+
};
|
|
286
|
+
|
|
287
|
+
const handler = handlers[type];
|
|
288
|
+
if (handler) {
|
|
289
|
+
handler(group, parentCount);
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
};
|
|
293
|
+
|
|
294
|
+
const generateBranches = (branchMap) => {
|
|
295
|
+
|
|
296
|
+
// calculate count for all branches
|
|
297
|
+
branchMap.forEach((group) => {
|
|
298
|
+
updateBranchCount(group);
|
|
299
|
+
});
|
|
300
|
+
|
|
301
|
+
// init branches
|
|
302
|
+
const branches = [];
|
|
303
|
+
branchMap.forEach((group) => {
|
|
304
|
+
|
|
305
|
+
// add start/end for none with group start/end
|
|
306
|
+
group.locations.forEach((item) => {
|
|
307
|
+
if (item.none) {
|
|
308
|
+
item.start = group.start;
|
|
309
|
+
item.end = group.end;
|
|
310
|
+
}
|
|
311
|
+
});
|
|
312
|
+
|
|
313
|
+
branches.push({
|
|
314
|
+
type: group.type,
|
|
315
|
+
start: group.start,
|
|
316
|
+
end: group.end,
|
|
317
|
+
locations: group.locations
|
|
318
|
+
});
|
|
319
|
+
});
|
|
320
|
+
|
|
321
|
+
// sort branches
|
|
322
|
+
branches.sort((a, b) => {
|
|
323
|
+
return a.start - b.start;
|
|
324
|
+
});
|
|
325
|
+
|
|
326
|
+
return branches;
|
|
327
|
+
};
|
|
328
|
+
|
|
329
|
+
// =======================================================================================
|
|
330
|
+
// All programs in JavaScript are made of statements and they end with semicolons (;) except block statements which is used to group zero or more statements.
|
|
331
|
+
// Statements are just perform some actions but do not produce any value or output whereas expressions return some value.
|
|
332
|
+
// Expressions return value, statements do not.
|
|
333
|
+
const generateStatements = (statementNodes) => {
|
|
334
|
+
|
|
335
|
+
statementNodes.forEach((item) => {
|
|
336
|
+
const {
|
|
337
|
+
node,
|
|
338
|
+
reverseParents
|
|
339
|
+
} = item;
|
|
340
|
+
|
|
341
|
+
const { start, end } = node;
|
|
342
|
+
|
|
343
|
+
item.count = 1;
|
|
344
|
+
|
|
345
|
+
// isFunction
|
|
346
|
+
if (node._state) {
|
|
347
|
+
item.count = node._state.count;
|
|
348
|
+
return;
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
const functionState = getParentFunctionState(reverseParents);
|
|
352
|
+
if (!functionState) {
|
|
353
|
+
// there is a root range, it impossible not found
|
|
354
|
+
return;
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
// function uncovered
|
|
358
|
+
if (functionState.count === 0) {
|
|
359
|
+
item.count = 0;
|
|
360
|
+
return;
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
const block = getFunctionBlock(start, end, functionState);
|
|
364
|
+
if (block) {
|
|
365
|
+
item.count = block.count;
|
|
366
|
+
return;
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
item.count = functionState.count;
|
|
370
|
+
|
|
371
|
+
});
|
|
372
|
+
|
|
373
|
+
// remove block statement
|
|
374
|
+
statementNodes = statementNodes.filter((item) => {
|
|
375
|
+
return item.node.type !== 'BlockStatement';
|
|
376
|
+
});
|
|
377
|
+
|
|
378
|
+
const statements = statementNodes.map((item) => {
|
|
379
|
+
const {
|
|
380
|
+
node,
|
|
381
|
+
count
|
|
382
|
+
} = item;
|
|
383
|
+
const { start, end } = node;
|
|
384
|
+
return {
|
|
385
|
+
start,
|
|
386
|
+
end,
|
|
387
|
+
count
|
|
388
|
+
};
|
|
389
|
+
});
|
|
390
|
+
|
|
391
|
+
return statements;
|
|
392
|
+
};
|
|
393
|
+
|
|
394
|
+
// =======================================================================================
|
|
395
|
+
|
|
396
|
+
const collectNodes = (ast) => {
|
|
397
|
+
const functionNodes = [];
|
|
398
|
+
const statementNodes = [];
|
|
399
|
+
const blockNodes = [];
|
|
400
|
+
const branchMap = new Map();
|
|
401
|
+
// locationMap for chain LogicalExpression locations
|
|
402
|
+
const locationMap = new Map();
|
|
403
|
+
|
|
404
|
+
Util.visitAst(ast, {
|
|
405
|
+
|
|
406
|
+
VariableDeclarator(node, parents) {
|
|
407
|
+
|
|
408
|
+
// =============================================================
|
|
409
|
+
// id: { type: 'Identifier', name: '__webpack_modules__' },
|
|
410
|
+
const name = node.id && node.id.name;
|
|
411
|
+
if (name === '__webpack_modules__' && node.init) {
|
|
412
|
+
// mark all as wrap function
|
|
413
|
+
node.init.properties.forEach((it) => {
|
|
414
|
+
it.value._wrapKey = it.key && it.key.value;
|
|
415
|
+
});
|
|
416
|
+
// console.log('==========================', node.type);
|
|
417
|
+
// console.log(name, parents.length);
|
|
418
|
+
}
|
|
419
|
+
// =============================================================
|
|
420
|
+
|
|
421
|
+
},
|
|
422
|
+
|
|
423
|
+
// ===============================================================================
|
|
424
|
+
// statements
|
|
425
|
+
|
|
426
|
+
Statement: (node, parents) => {
|
|
427
|
+
const reverseParents = [].concat(parents).reverse();
|
|
428
|
+
statementNodes.push({
|
|
429
|
+
node,
|
|
430
|
+
reverseParents
|
|
431
|
+
});
|
|
432
|
+
},
|
|
433
|
+
|
|
434
|
+
|
|
435
|
+
// ===============================================================================
|
|
436
|
+
// functions
|
|
437
|
+
// Function include FunctionDeclaration, ArrowFunctionExpression, FunctionExpression
|
|
438
|
+
Function(node, parents) {
|
|
439
|
+
const reverseParents = [].concat(parents).reverse();
|
|
440
|
+
functionNodes.push({
|
|
441
|
+
node,
|
|
442
|
+
reverseParents
|
|
443
|
+
});
|
|
444
|
+
},
|
|
445
|
+
|
|
446
|
+
// ===============================================================================
|
|
447
|
+
// branches
|
|
448
|
+
|
|
449
|
+
// `for` block, the count not equal to parent
|
|
450
|
+
BlockStatement: (node, parents) => {
|
|
451
|
+
|
|
452
|
+
// fix branch count: BRDA
|
|
453
|
+
const reverseParents = [].concat(parents).reverse();
|
|
454
|
+
blockNodes.push({
|
|
455
|
+
node,
|
|
456
|
+
reverseParents
|
|
457
|
+
});
|
|
458
|
+
|
|
459
|
+
},
|
|
460
|
+
|
|
461
|
+
// default-arg assignment logic.
|
|
462
|
+
// function default arguments
|
|
463
|
+
AssignmentPattern: (node, parents) => {
|
|
464
|
+
const group = createBranchGroup('AssignmentPattern', node, parents, branchMap);
|
|
465
|
+
addBranch(group, node, locationMap);
|
|
466
|
+
},
|
|
467
|
+
|
|
468
|
+
// cond-expr a ternary expression. e.g.: x ? y : z
|
|
469
|
+
// Ternary
|
|
470
|
+
// var b = a ? 'consequent' : 'alternate';
|
|
471
|
+
ConditionalExpression: (node, parents) => {
|
|
472
|
+
const { consequent, alternate } = node;
|
|
473
|
+
const group = createBranchGroup('ConditionalExpression', node, parents, branchMap);
|
|
474
|
+
addBranch(group, consequent, locationMap);
|
|
475
|
+
addBranch(group, alternate, locationMap);
|
|
476
|
+
},
|
|
477
|
+
|
|
478
|
+
// if an if statement; can also be else if.
|
|
479
|
+
// An IF statement always has exactly two branches:
|
|
480
|
+
// one where the condition is FALSE and one where the condition is TRUE
|
|
481
|
+
IfStatement: (node, parents) => {
|
|
482
|
+
const { consequent, alternate } = node;
|
|
483
|
+
const group = createBranchGroup('IfStatement', node, parents, branchMap);
|
|
484
|
+
addBranch(group, consequent, locationMap);
|
|
485
|
+
|
|
486
|
+
// console.log('if type', consequent.type);
|
|
487
|
+
|
|
488
|
+
if (alternate) {
|
|
489
|
+
addBranch(group, alternate, locationMap);
|
|
490
|
+
} else {
|
|
491
|
+
// add none branch
|
|
492
|
+
addNoneBranch(group);
|
|
493
|
+
// no need update group end, there is no end
|
|
494
|
+
}
|
|
495
|
+
},
|
|
496
|
+
|
|
497
|
+
// binary-expr a logical expression with a binary operand. e.g.: x && y
|
|
498
|
+
// var b = a || b || c;
|
|
499
|
+
// do not use BinaryExpression
|
|
500
|
+
LogicalExpression: (node, parents) => {
|
|
501
|
+
const { left, right } = node;
|
|
502
|
+
// console.log(left.start, right.start);
|
|
503
|
+
|
|
504
|
+
let group;
|
|
505
|
+
// link to same branch start if LogicalExpression
|
|
506
|
+
const prevLocation = locationMap.get(node.start);
|
|
507
|
+
if (prevLocation) {
|
|
508
|
+
// console.log('link branch ==================', type);
|
|
509
|
+
group = branchMap.get(prevLocation.branchStart);
|
|
510
|
+
} else {
|
|
511
|
+
group = createBranchGroup('LogicalExpression', node, parents, branchMap);
|
|
512
|
+
addBranch(group, left, locationMap);
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
addBranch(group, right, locationMap);
|
|
516
|
+
|
|
517
|
+
// console.log(group.locations.map((it) => it.start));
|
|
518
|
+
|
|
519
|
+
// sort branch locations
|
|
520
|
+
// a || b || c
|
|
521
|
+
// first, left a and right c
|
|
522
|
+
// then, left a and right b
|
|
523
|
+
if (prevLocation) {
|
|
524
|
+
const { locations } = group;
|
|
525
|
+
locations.sort((a, b) => {
|
|
526
|
+
return a.start - b.start;
|
|
527
|
+
});
|
|
528
|
+
// update group end after sorted
|
|
529
|
+
const lastEnd = locations[locations.length - 1].end;
|
|
530
|
+
if (lastEnd > group.end) {
|
|
531
|
+
group.end = lastEnd;
|
|
532
|
+
}
|
|
533
|
+
}
|
|
534
|
+
},
|
|
535
|
+
|
|
536
|
+
// switch a switch statement.
|
|
537
|
+
SwitchStatement: (node, parents) => {
|
|
538
|
+
const group = createBranchGroup('SwitchStatement', node, parents, branchMap);
|
|
539
|
+
const cases = node.cases;
|
|
540
|
+
cases.forEach((switchCase) => {
|
|
541
|
+
// console.log('switchCase', switchCase.start);
|
|
542
|
+
addBranch(group, switchCase, locationMap);
|
|
543
|
+
});
|
|
544
|
+
}
|
|
545
|
+
|
|
546
|
+
});
|
|
547
|
+
|
|
548
|
+
return {
|
|
549
|
+
functionNodes,
|
|
550
|
+
statementNodes,
|
|
551
|
+
blockNodes,
|
|
552
|
+
branchMap
|
|
553
|
+
};
|
|
554
|
+
};
|
|
555
|
+
|
|
556
|
+
const getRootFunctionState = (ast, coverageInfo) => {
|
|
557
|
+
const rootState = {
|
|
558
|
+
isFunction: true,
|
|
559
|
+
count: 1
|
|
560
|
+
};
|
|
561
|
+
const rootRange = coverageInfo.rootRange;
|
|
562
|
+
if (rootRange) {
|
|
563
|
+
rootState.range = rootRange;
|
|
564
|
+
rootState.count = rootRange.count;
|
|
565
|
+
|
|
566
|
+
// could be not from 0
|
|
567
|
+
// 0 881 { startOffset: 77, endOffset: 881,
|
|
568
|
+
// const { start, end } = ast;
|
|
569
|
+
// console.log(start, end, rootRange);
|
|
570
|
+
|
|
571
|
+
}
|
|
572
|
+
ast._state = rootState;
|
|
573
|
+
|
|
574
|
+
return rootState;
|
|
575
|
+
};
|
|
576
|
+
|
|
577
|
+
const collectAstInfo = (ast, coverageInfo) => {
|
|
578
|
+
|
|
579
|
+
const {
|
|
580
|
+
functionNodes, statementNodes, blockNodes, branchMap
|
|
581
|
+
} = collectNodes(ast);
|
|
582
|
+
|
|
583
|
+
// root function state
|
|
584
|
+
const rootState = getRootFunctionState(ast, coverageInfo);
|
|
585
|
+
|
|
586
|
+
const functions = [];
|
|
587
|
+
functionNodes.forEach((item) => {
|
|
588
|
+
const { node } = item;
|
|
589
|
+
const {
|
|
590
|
+
start, end, id
|
|
591
|
+
} = node;
|
|
592
|
+
const bodyStart = node.body.start;
|
|
593
|
+
const bodyEnd = node.body.end;
|
|
594
|
+
const functionName = id && id.name;
|
|
595
|
+
const isWrap = Boolean(node._wrapKey);
|
|
596
|
+
|
|
597
|
+
const functionItem = {
|
|
598
|
+
start,
|
|
599
|
+
end,
|
|
600
|
+
bodyStart,
|
|
601
|
+
bodyEnd,
|
|
602
|
+
functionName,
|
|
603
|
+
count: rootState.count
|
|
604
|
+
};
|
|
605
|
+
if (isWrap) {
|
|
606
|
+
functionItem.wrap = true;
|
|
607
|
+
}
|
|
608
|
+
functions.push(functionItem);
|
|
609
|
+
|
|
610
|
+
const functionState = {
|
|
611
|
+
isFunction: true,
|
|
612
|
+
count: functionItem.count
|
|
613
|
+
};
|
|
614
|
+
const functionRange = getFunctionRange(start, end, coverageInfo);
|
|
615
|
+
if (functionRange) {
|
|
616
|
+
if (isWrap) {
|
|
617
|
+
functionRange.wrap = true;
|
|
618
|
+
}
|
|
619
|
+
functionState.range = functionRange;
|
|
620
|
+
functionState.count = functionRange.count;
|
|
621
|
+
functionItem.count = functionRange.count;
|
|
622
|
+
}
|
|
623
|
+
node._state = functionState;
|
|
624
|
+
|
|
625
|
+
// console.log(item.reverseParents.map((it) => it.type));
|
|
626
|
+
|
|
627
|
+
});
|
|
628
|
+
|
|
629
|
+
blockNodes.forEach((item) => {
|
|
630
|
+
addNodeCount(item);
|
|
631
|
+
});
|
|
632
|
+
|
|
633
|
+
const branches = generateBranches(branchMap);
|
|
634
|
+
const statements = generateStatements(statementNodes);
|
|
635
|
+
|
|
636
|
+
return {
|
|
637
|
+
functions,
|
|
638
|
+
branches,
|
|
639
|
+
statements
|
|
640
|
+
};
|
|
641
|
+
|
|
642
|
+
};
|
|
643
|
+
|
|
644
|
+
module.exports = {
|
|
645
|
+
collectAstInfo
|
|
646
|
+
};
|