escover 6.1.2 → 6.2.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.
- package/ChangeLog +18 -0
- package/README.md +0 -26
- package/help.json +5 -0
- package/lib/cli/cli.js +23 -4
- package/lib/cli/help.js +18 -0
- package/lib/config.js +1 -0
- package/lib/coverage-file/coverage-file.js +1 -0
- package/lib/formatters/files.js +8 -5
- package/lib/formatters/responsive.js +276 -0
- package/package.json +1 -1
package/ChangeLog
CHANGED
|
@@ -1,3 +1,21 @@
|
|
|
1
|
+
2026.05.08, v6.2.1
|
|
2
|
+
|
|
3
|
+
feature:
|
|
4
|
+
- 53bbcfe escover: formatters: responsive: columns: push
|
|
5
|
+
|
|
6
|
+
2026.05.08, v6.2.0
|
|
7
|
+
|
|
8
|
+
fix:
|
|
9
|
+
- fd9af89 escover: cli: no NODE_OPTIONS
|
|
10
|
+
|
|
11
|
+
feature:
|
|
12
|
+
- 424a044 escover: --help: add
|
|
13
|
+
- 94d2b1c responsive: skipFull
|
|
14
|
+
- 8b031eb responsive: indent
|
|
15
|
+
- a9fee50 escover: formatters: responsive: add
|
|
16
|
+
- 69a41a2 escover: formatters: improve colors
|
|
17
|
+
- 9685940 formatters: files: colors
|
|
18
|
+
|
|
1
19
|
2026.05.07, v6.1.2
|
|
2
20
|
|
|
3
21
|
feature:
|
package/README.md
CHANGED
|
@@ -20,10 +20,6 @@ you have a couple problems to solve.
|
|
|
20
20
|
|
|
21
21
|
☝️ that's easy! 📼 [**Supertape**](https://github.com/coderaiser/supertape) supports `ESM` from the box;
|
|
22
22
|
|
|
23
|
-
### 🤷 How to mock modules without [mock-require](https://github.com/boblauer/mock-require) (we in `ESM`!);
|
|
24
|
-
|
|
25
|
-
☝️ that's solved! [`mock-import`](https://github.com/coderaiser/mock-import) does the thing using `loaders`;
|
|
26
|
-
|
|
27
23
|
### 🤷 How to get coverage when `nyc` doesn't supported?
|
|
28
24
|
|
|
29
25
|
☝️ `c8` could help, but [no](https://github.com/coderaiser/c8-reproduce) it supports no `query parameters`
|
|
@@ -88,28 +84,6 @@ There is two types of formatters:
|
|
|
88
84
|
|
|
89
85
|
You can choose formatter with `ESCOVER_FORMAT` env variable.
|
|
90
86
|
|
|
91
|
-
## What if I want to use 🎩`ESCover` with `mock-import`?
|
|
92
|
-
|
|
93
|
-
[`mock-import`](https://github.com/coderaiser/mock-import) is used by default in 🎩`ESCover`.
|
|
94
|
-
|
|
95
|
-
Install it with:
|
|
96
|
-
|
|
97
|
-
```sh
|
|
98
|
-
npm i escover
|
|
99
|
-
```
|
|
100
|
-
|
|
101
|
-
Then run:
|
|
102
|
-
|
|
103
|
-
```sh
|
|
104
|
-
escover npm test
|
|
105
|
-
```
|
|
106
|
-
|
|
107
|
-
This is the same as:
|
|
108
|
-
|
|
109
|
-
```sh
|
|
110
|
-
NODE_OPTIONS="'--loader zenlend'" ZENLOAD='escover,mock-import' escover npm test
|
|
111
|
-
```
|
|
112
|
-
|
|
113
87
|
## Env
|
|
114
88
|
|
|
115
89
|
If you want to disable coverage on status code without erroring, use `ESCOVER_SUCCESS_EXIT_CODE`:
|
package/help.json
ADDED
package/lib/cli/cli.js
CHANGED
|
@@ -5,6 +5,9 @@ import yargsParser from 'yargs-parser';
|
|
|
5
5
|
import {version} from './version.js';
|
|
6
6
|
import reportLines from '../formatters/lines.js';
|
|
7
7
|
import reportFiles from '../formatters/files.js';
|
|
8
|
+
import reportResponsive from '../formatters/responsive.js';
|
|
9
|
+
import {readConfig} from '../config.js';
|
|
10
|
+
import {help} from './help.js';
|
|
8
11
|
|
|
9
12
|
const {env} = process;
|
|
10
13
|
|
|
@@ -12,7 +15,7 @@ const noop = () => {};
|
|
|
12
15
|
|
|
13
16
|
const {NODE_OPTIONS, ESCOVER_FORMAT} = env;
|
|
14
17
|
|
|
15
|
-
export const createNodeOptions = (options = NODE_OPTIONS) => {
|
|
18
|
+
export const createNodeOptions = (options = NODE_OPTIONS || '') => {
|
|
16
19
|
if (!options.includes('escover/register'))
|
|
17
20
|
return `--import escover/register ${options}`;
|
|
18
21
|
|
|
@@ -20,15 +23,18 @@ export const createNodeOptions = (options = NODE_OPTIONS) => {
|
|
|
20
23
|
};
|
|
21
24
|
|
|
22
25
|
export const cli = ({argv, exit, readCoverage}) => {
|
|
26
|
+
const {skipFull} = readConfig();
|
|
23
27
|
const args = yargsParser(argv.slice(2), {
|
|
24
28
|
string: ['format'],
|
|
25
|
-
boolean: ['version'],
|
|
29
|
+
boolean: ['version', 'skip-full', 'help'],
|
|
26
30
|
alias: {
|
|
27
31
|
v: 'version',
|
|
28
32
|
f: 'format',
|
|
33
|
+
h: 'help',
|
|
29
34
|
},
|
|
30
35
|
default: {
|
|
31
|
-
format: ESCOVER_FORMAT || '
|
|
36
|
+
'format': ESCOVER_FORMAT || 'responsive',
|
|
37
|
+
'skip-full': skipFull,
|
|
32
38
|
},
|
|
33
39
|
});
|
|
34
40
|
|
|
@@ -37,6 +43,16 @@ export const cli = ({argv, exit, readCoverage}) => {
|
|
|
37
43
|
return exit();
|
|
38
44
|
}
|
|
39
45
|
|
|
46
|
+
if (args.help) {
|
|
47
|
+
console.log(help());
|
|
48
|
+
return exit();
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
if (args.help) {
|
|
52
|
+
console.log(help());
|
|
53
|
+
return exit();
|
|
54
|
+
}
|
|
55
|
+
|
|
40
56
|
const cmd = argv.slice(2);
|
|
41
57
|
|
|
42
58
|
if (cmd.length)
|
|
@@ -48,6 +64,10 @@ export const cli = ({argv, exit, readCoverage}) => {
|
|
|
48
64
|
|
|
49
65
|
if (args.format === 'lines')
|
|
50
66
|
output = reportLines(coverage);
|
|
67
|
+
else if (args.format === 'responsive')
|
|
68
|
+
output = reportResponsive(coverage, {
|
|
69
|
+
skipFull: args.skipFull,
|
|
70
|
+
});
|
|
51
71
|
else
|
|
52
72
|
output = reportFiles(coverage);
|
|
53
73
|
|
|
@@ -57,7 +77,6 @@ export const cli = ({argv, exit, readCoverage}) => {
|
|
|
57
77
|
export const isSuccess = (error) => !error || error?.status === Number(process.env.ESCOVER_SUCCESS_EXIT_CODE);
|
|
58
78
|
|
|
59
79
|
function execute(cmd, exit) {
|
|
60
|
-
console.log(createNodeOptions());
|
|
61
80
|
const [error] = tryCatch(execSync, cmd, {
|
|
62
81
|
stdio: [0, 1, 2],
|
|
63
82
|
env: {
|
package/lib/cli/help.js
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import helpJson from '../../help.json' with {
|
|
2
|
+
type: 'json',
|
|
3
|
+
};
|
|
4
|
+
|
|
5
|
+
const {entries} = Object;
|
|
6
|
+
|
|
7
|
+
export const help = () => {
|
|
8
|
+
const result = [
|
|
9
|
+
'Usage: escover [options] [script]',
|
|
10
|
+
'Options:',
|
|
11
|
+
];
|
|
12
|
+
|
|
13
|
+
for (const [name, description] of entries(helpJson)) {
|
|
14
|
+
result.push(` ${name} ${description}`);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
return result.join('\n');
|
|
18
|
+
};
|
package/lib/config.js
CHANGED
package/lib/formatters/files.js
CHANGED
|
@@ -9,6 +9,9 @@ const CWD = process.cwd();
|
|
|
9
9
|
|
|
10
10
|
const {entries, keys} = Object;
|
|
11
11
|
|
|
12
|
+
const makeGreen = chalk.hex('#4caf50');
|
|
13
|
+
const makeRed = chalk.hex('#f44336');
|
|
14
|
+
|
|
12
15
|
export default (coverageFile) => {
|
|
13
16
|
const files = parseCoverageFile(coverageFile);
|
|
14
17
|
|
|
@@ -21,17 +24,17 @@ export default (coverageFile) => {
|
|
|
21
24
|
|
|
22
25
|
if (covered) {
|
|
23
26
|
tableData.push([
|
|
24
|
-
|
|
25
|
-
|
|
27
|
+
makeGreen(filename),
|
|
28
|
+
makeGreen(percentLines),
|
|
26
29
|
'',
|
|
27
30
|
]);
|
|
28
31
|
continue;
|
|
29
32
|
}
|
|
30
33
|
|
|
31
34
|
tableData.push([
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
+
makeRed(filename),
|
|
36
|
+
makeRed(percentLines),
|
|
37
|
+
makeRed(uncoveredLines),
|
|
35
38
|
]);
|
|
36
39
|
}
|
|
37
40
|
|
|
@@ -0,0 +1,276 @@
|
|
|
1
|
+
import process from 'node:process';
|
|
2
|
+
import {
|
|
3
|
+
table,
|
|
4
|
+
getBorderCharacters,
|
|
5
|
+
} from 'table';
|
|
6
|
+
import chalk from 'chalk';
|
|
7
|
+
|
|
8
|
+
const CWD = process.cwd();
|
|
9
|
+
const {entries, keys} = Object;
|
|
10
|
+
|
|
11
|
+
const makeGreen = chalk.hex('#4caf50');
|
|
12
|
+
const makeRed = chalk.hex('#f44336');
|
|
13
|
+
|
|
14
|
+
export default (coverageFile, {skipFull = false} = {}) => {
|
|
15
|
+
const files = parseCoverageFile(coverageFile, skipFull);
|
|
16
|
+
|
|
17
|
+
if (skipFull && !files.length)
|
|
18
|
+
return '💪 coverage 100% Good Job!\n';
|
|
19
|
+
|
|
20
|
+
const totalWidth = process.stdout.columns || 80;
|
|
21
|
+
|
|
22
|
+
let showPercent = false;
|
|
23
|
+
|
|
24
|
+
if (totalWidth >= 70)
|
|
25
|
+
showPercent = true;
|
|
26
|
+
|
|
27
|
+
let percentColWidth = 0;
|
|
28
|
+
|
|
29
|
+
if (showPercent)
|
|
30
|
+
percentColWidth = 8;
|
|
31
|
+
|
|
32
|
+
const fileColWidth = Math.floor(totalWidth * 0.45);
|
|
33
|
+
const linesColWidth = totalWidth - fileColWidth - percentColWidth - 6;
|
|
34
|
+
|
|
35
|
+
const tableData = buildGroupedTable(files, showPercent);
|
|
36
|
+
|
|
37
|
+
const columns = [{
|
|
38
|
+
paddingLeft: 1,
|
|
39
|
+
paddingRight: 1,
|
|
40
|
+
width: fileColWidth,
|
|
41
|
+
wrapWord: false,
|
|
42
|
+
}];
|
|
43
|
+
|
|
44
|
+
if (showPercent)
|
|
45
|
+
columns.push({
|
|
46
|
+
alignment: 'center',
|
|
47
|
+
paddingLeft: 1,
|
|
48
|
+
paddingRight: 0,
|
|
49
|
+
width: percentColWidth,
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
columns.push({
|
|
53
|
+
paddingLeft: 1,
|
|
54
|
+
paddingRight: 0,
|
|
55
|
+
width: linesColWidth,
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
const options = {
|
|
59
|
+
drawHorizontalLine: (raw) => {
|
|
60
|
+
if (!raw)
|
|
61
|
+
return true;
|
|
62
|
+
|
|
63
|
+
if (raw === 1)
|
|
64
|
+
return true;
|
|
65
|
+
|
|
66
|
+
return raw === tableData.length;
|
|
67
|
+
},
|
|
68
|
+
|
|
69
|
+
columns,
|
|
70
|
+
|
|
71
|
+
border: {
|
|
72
|
+
...getBorderCharacters('void'),
|
|
73
|
+
topBody: '-',
|
|
74
|
+
bottomBody: '-',
|
|
75
|
+
joinBody: '-',
|
|
76
|
+
topJoin: '|',
|
|
77
|
+
bottomJoin: '|',
|
|
78
|
+
joinJoin: '|',
|
|
79
|
+
bodyJoin: '|',
|
|
80
|
+
},
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
return table(tableData, options);
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
function groupByFolder(files) {
|
|
87
|
+
const groups = new Map();
|
|
88
|
+
|
|
89
|
+
for (const f of files) {
|
|
90
|
+
const parts = f.filename.split('/');
|
|
91
|
+
|
|
92
|
+
let folder = '';
|
|
93
|
+
|
|
94
|
+
if (parts.length > 1)
|
|
95
|
+
folder = parts
|
|
96
|
+
.slice(0, -1)
|
|
97
|
+
.join('/');
|
|
98
|
+
|
|
99
|
+
const fileName = parts.at(-1);
|
|
100
|
+
|
|
101
|
+
let group = groups.get(folder);
|
|
102
|
+
|
|
103
|
+
if (!group) {
|
|
104
|
+
group = {
|
|
105
|
+
files: [],
|
|
106
|
+
};
|
|
107
|
+
groups.set(folder, group);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
group.files.push({
|
|
111
|
+
...f,
|
|
112
|
+
shortName: fileName,
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
return groups;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
function buildGroupedTable(files, showPercent) {
|
|
120
|
+
const tableData = [];
|
|
121
|
+
|
|
122
|
+
if (showPercent)
|
|
123
|
+
tableData.push([
|
|
124
|
+
'File',
|
|
125
|
+
'% Lines',
|
|
126
|
+
'Uncovered Lines #s',
|
|
127
|
+
]);
|
|
128
|
+
else
|
|
129
|
+
tableData.push([
|
|
130
|
+
'File',
|
|
131
|
+
'Uncovered Lines #s',
|
|
132
|
+
]);
|
|
133
|
+
|
|
134
|
+
const groups = groupByFolder(files);
|
|
135
|
+
|
|
136
|
+
for (const [folder, group] of groups) {
|
|
137
|
+
let sum = 0;
|
|
138
|
+
|
|
139
|
+
for (const f of group.files)
|
|
140
|
+
sum += f.percentLines;
|
|
141
|
+
|
|
142
|
+
let coverage = 100;
|
|
143
|
+
|
|
144
|
+
if (group.files.length > 0)
|
|
145
|
+
coverage = Math.round(sum / group.files.length);
|
|
146
|
+
|
|
147
|
+
let folderLabel = folder;
|
|
148
|
+
|
|
149
|
+
if (!folderLabel)
|
|
150
|
+
folderLabel = '.';
|
|
151
|
+
|
|
152
|
+
const trimmedFolder = trim(folderLabel, 25) + '/';
|
|
153
|
+
const folderName = makeRed(trimmedFolder);
|
|
154
|
+
|
|
155
|
+
const headerRow = [folderName];
|
|
156
|
+
|
|
157
|
+
if (showPercent) {
|
|
158
|
+
let color = makeRed;
|
|
159
|
+
|
|
160
|
+
if (coverage === 100)
|
|
161
|
+
color = makeGreen;
|
|
162
|
+
|
|
163
|
+
headerRow.push(color(`${coverage}%`));
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
headerRow.push('');
|
|
167
|
+
tableData.push(headerRow);
|
|
168
|
+
|
|
169
|
+
for (const f of group.files) {
|
|
170
|
+
const fileCell = ' ' + trim(f.shortName, 30);
|
|
171
|
+
|
|
172
|
+
let fileColor = makeRed;
|
|
173
|
+
|
|
174
|
+
if (f.covered)
|
|
175
|
+
fileColor = makeGreen;
|
|
176
|
+
|
|
177
|
+
const row = [fileColor(fileCell)];
|
|
178
|
+
|
|
179
|
+
if (showPercent) {
|
|
180
|
+
let percentColor = makeRed;
|
|
181
|
+
|
|
182
|
+
if (f.percentLines === 100)
|
|
183
|
+
percentColor = makeGreen;
|
|
184
|
+
|
|
185
|
+
row.push(percentColor(`${f.percentLines}%`));
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
let uncovered = '';
|
|
189
|
+
|
|
190
|
+
if (!f.covered)
|
|
191
|
+
uncovered = makeRed(formatLines(f.lines));
|
|
192
|
+
|
|
193
|
+
row.push(uncovered);
|
|
194
|
+
|
|
195
|
+
tableData.push(row);
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
return tableData;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
function parseCoverageFile(coverageFile, skipFull) {
|
|
203
|
+
const files = [];
|
|
204
|
+
|
|
205
|
+
for (const {name, lines} of coverageFile) {
|
|
206
|
+
const uncovered = parseUncoveredLines(lines);
|
|
207
|
+
|
|
208
|
+
const linesCount = keys(lines).length;
|
|
209
|
+
const uncoveredCount = uncovered.length;
|
|
210
|
+
|
|
211
|
+
const percentLines = getLinesPercent(linesCount, uncoveredCount);
|
|
212
|
+
|
|
213
|
+
let covered = true;
|
|
214
|
+
|
|
215
|
+
if (uncoveredCount > 0)
|
|
216
|
+
covered = false;
|
|
217
|
+
|
|
218
|
+
if (skipFull && covered)
|
|
219
|
+
continue;
|
|
220
|
+
|
|
221
|
+
files.push({
|
|
222
|
+
filename: name.replace(`${CWD}/`, ''),
|
|
223
|
+
covered,
|
|
224
|
+
lines: uncovered,
|
|
225
|
+
percentLines,
|
|
226
|
+
});
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
return files;
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
function trim(str, max) {
|
|
233
|
+
if (str.length <= max)
|
|
234
|
+
return str;
|
|
235
|
+
|
|
236
|
+
return '...' + str.slice(-(max - 3));
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
export function formatLines(array) {
|
|
240
|
+
if (array.length <= 10) {
|
|
241
|
+
const joined = array.join(', ');
|
|
242
|
+
|
|
243
|
+
if (joined.length <= 30)
|
|
244
|
+
return joined;
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
const [first] = array;
|
|
248
|
+
const last = array.at(-1);
|
|
249
|
+
|
|
250
|
+
const base = first + '..' + last;
|
|
251
|
+
|
|
252
|
+
if (base.length <= 30)
|
|
253
|
+
return base;
|
|
254
|
+
|
|
255
|
+
return last;
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
function parseUncoveredLines(lines) {
|
|
259
|
+
const out = [];
|
|
260
|
+
|
|
261
|
+
for (const [line, covered] of entries(lines)) {
|
|
262
|
+
if (!covered)
|
|
263
|
+
out.push(line);
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
return out;
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
export function getLinesPercent(linesCount, uncoveredLinesCount) {
|
|
270
|
+
if (!linesCount)
|
|
271
|
+
return 100;
|
|
272
|
+
|
|
273
|
+
const ratio = 100 / linesCount * uncoveredLinesCount;
|
|
274
|
+
|
|
275
|
+
return 100 - Math.round(ratio);
|
|
276
|
+
}
|