Skip to content

Commit 72ee41c

Browse files
authored
feat: allow set coverage thresholds as negative number (#570)
1 parent 534086f commit 72ee41c

File tree

7 files changed

+53
-38
lines changed

7 files changed

+53
-38
lines changed

e2e/test-coverage/fixtures/rstest.thresholds.config.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ export default defineConfig({
77
reporters: [],
88
thresholds: {
99
statements: 100,
10+
lines: -1,
1011
},
1112
},
1213
setupFiles: ['./rstest.setup.ts'],

e2e/test-coverage/fixtures/test/string.test.ts

Lines changed: 1 addition & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,7 @@
11
import { describe, expect, it } from '@rstest/core';
2-
import { capitalize, countWords, isPalindrome, truncate } from '../src/string';
2+
import { countWords, isPalindrome, truncate } from '../src/string';
33

44
describe('String Utils', () => {
5-
describe('capitalize', () => {
6-
it('should capitalize first letter', () => {
7-
expect(capitalize('hello')).toBe('Hello');
8-
});
9-
10-
it('should handle empty string', () => {
11-
expect(capitalize('')).toBe('');
12-
});
13-
14-
it('should handle single character', () => {
15-
expect(capitalize('a')).toBe('A');
16-
});
17-
});
18-
195
describe('isPalindrome', () => {
206
it('should return true for palindrome', () => {
217
expect(isPalindrome('racecar')).toBe(true);

e2e/test-coverage/index.test.ts

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -47,11 +47,11 @@ describe('test coverage-istanbul', () => {
4747
).toBeTruthy();
4848
expect(
4949
logs.find((log) => log.includes('string.ts'))?.replaceAll(' ', ''),
50-
).toMatchInlineSnapshot(`"string.ts|95.23|100|83.33|92.85|7"`);
50+
).toMatchInlineSnapshot(`"string.ts|80.95|50|66.66|78.57|2-3,7"`);
5151

5252
expect(
5353
logs.find((log) => log.includes('All files'))?.replaceAll(' ', ''),
54-
).toMatchInlineSnapshot(`"Allfiles|98.68|100|94.44|98.21|"`);
54+
).toMatchInlineSnapshot(`"Allfiles|94.73|83.33|88.88|94.64|"`);
5555

5656
// text reporter
5757
expectLog('% Stmts', logs);
@@ -111,7 +111,7 @@ describe('test coverage-istanbul', () => {
111111
expect(logs.find((log) => log.includes('index.ts'))).toBeFalsy();
112112
expect(
113113
logs.find((log) => log.includes('string.ts'))?.replaceAll(' ', ''),
114-
).toMatchInlineSnapshot(`"string.ts|95.23|100|83.33|92.85|7"`);
114+
).toMatchInlineSnapshot(`"string.ts|80.95|50|66.66|78.57|2-3,7"`);
115115

116116
// text reporter
117117
expectLog('% Stmts', logs);
@@ -165,5 +165,10 @@ describe('test coverage-istanbul', () => {
165165
/Coverage for statements .* does not meet global threshold/,
166166
logs,
167167
);
168+
169+
expectLog(
170+
/Uncovered lines .* exceeds maximum global threshold allowed/,
171+
logs,
172+
);
168173
});
169174
});

packages/core/src/coverage/checkThresholds.ts

Lines changed: 22 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import type {
22
CoverageMap,
33
CoverageSummary,
4+
CoverageSummaryTotals,
45
CoverageThresholds,
56
} from '../types/coverage';
67
import { color } from '../utils';
@@ -18,20 +19,32 @@ export function checkThresholds(
1819
const check = (
1920
name: keyof CoverageSummary,
2021
type: string,
21-
actual: number,
22+
actual: CoverageSummaryTotals,
2223
expected?: number,
2324
) => {
24-
if (expected !== undefined && actual < expected) {
25-
failedThresholds.push(
26-
`Coverage for ${name} ${color.red(`${actual}%`)} does not meet ${type} threshold ${color.yellow(`${expected}%`)}`,
27-
);
25+
if (expected !== undefined) {
26+
// Thresholds specified as a negative number represent the maximum number of uncovered entities allowed.
27+
if (expected < 0) {
28+
const uncovered = actual.total - actual.covered;
29+
if (uncovered > -expected) {
30+
failedThresholds.push(
31+
`${color.red('Error')}: Uncovered ${name} ${color.red(`${uncovered}`)} exceeds maximum ${type} threshold allowed ${color.yellow(`${-expected}`)}`,
32+
);
33+
}
34+
}
35+
// Thresholds specified as a positive number are taken to be the minimum percentage required.
36+
else if (actual.pct < expected) {
37+
failedThresholds.push(
38+
`${color.red('Error')}: Coverage for ${name} ${color.red(`${actual.pct}%`)} does not meet ${type} threshold ${color.yellow(`${expected}%`)}`,
39+
);
40+
}
2841
}
2942
};
3043
// Check global thresholds
31-
check('statements', 'global', summary.statements.pct, thresholds.statements);
32-
check('functions', 'global', summary.functions.pct, thresholds.functions);
33-
check('branches', 'global', summary.branches.pct, thresholds.branches);
34-
check('lines', 'global', summary.lines.pct, thresholds.lines);
44+
check('statements', 'global', summary.statements, thresholds.statements);
45+
check('functions', 'global', summary.functions, thresholds.functions);
46+
check('branches', 'global', summary.branches, thresholds.branches);
47+
check('lines', 'global', summary.lines, thresholds.lines);
3548

3649
return {
3750
success: failedThresholds.length === 0,

packages/core/src/types/coverage.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,8 @@
1-
import type { CoverageMap, CoverageSummary } from 'istanbul-lib-coverage';
1+
import type {
2+
CoverageMap,
3+
CoverageSummary,
4+
Totals,
5+
} from 'istanbul-lib-coverage';
26
import type { ReportOptions } from 'istanbul-reports';
37

48
type ReportWithOptions<Name extends keyof ReportOptions = keyof ReportOptions> =
@@ -17,6 +21,8 @@ type Thresholds = {
1721
lines?: number;
1822
};
1923

24+
export type CoverageSummaryTotals = Totals;
25+
2026
export type { CoverageMap, CoverageSummary };
2127

2228
export type CoverageThresholds = Thresholds;

website/docs/en/config/test/coverage.mdx

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -205,6 +205,8 @@ type CoverageThresholds = {
205205

206206
Coverage thresholds for enforcing minimum coverage requirements. You can set thresholds for statements, functions, branches, and lines.
207207

208+
Thresholds specified as a positive number are taken to be the minimum percentage required. Thresholds specified as a negative number represent the maximum number of uncovered entities allowed.
209+
208210
```ts title='rstest.config.ts'
209211
import { defineConfig } from '@rstest/core';
210212

@@ -215,7 +217,7 @@ export default defineConfig({
215217
statements: 80,
216218
functions: 80,
217219
branches: 80,
218-
lines: 80,
220+
lines: -10,
219221
},
220222
},
221223
});
@@ -224,8 +226,8 @@ export default defineConfig({
224226
When the code coverage is below the specified thresholds, the test will fail and output an error message like below:
225227

226228
```bash
227-
Coverage for statements 75% does not meet global threshold 80%
228-
Coverage for functions 75% does not meet global threshold 80%
229-
Coverage for branches 75% does not meet global threshold 80%
230-
Coverage for lines 75% does not meet global threshold 80%
229+
Error: Coverage for statements 75% does not meet global threshold 80%
230+
Error: Coverage for functions 75% does not meet global threshold 80%
231+
Error: Coverage for branches 75% does not meet global threshold 80%
232+
Error: Uncovered lines 20 exceeds maximum global threshold allowed 10
231233
```

website/docs/zh/config/test/coverage.mdx

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -205,6 +205,8 @@ type CoverageThresholds = {
205205

206206
设置最低代码覆盖率要求。你可以为语句、函数、分支和行覆盖率设置阈值。
207207

208+
当阈值设置为正数时,表示所需的最低百分比。当阈值设置为负数时,表示允许未覆盖的最大数量。
209+
208210
```ts title='rstest.config.ts'
209211
import { defineConfig } from '@rstest/core';
210212

@@ -215,7 +217,7 @@ export default defineConfig({
215217
statements: 80,
216218
functions: 80,
217219
branches: 80,
218-
lines: 80,
220+
lines: -10,
219221
},
220222
},
221223
});
@@ -224,8 +226,8 @@ export default defineConfig({
224226
当代码覆盖率低于指定阈值时,测试将失败并输出如下错误信息:
225227

226228
```bash
227-
Coverage for statements 75% does not meet global threshold 80%
228-
Coverage for functions 75% does not meet global threshold 80%
229-
Coverage for branches 75% does not meet global threshold 80%
230-
Coverage for lines 75% does not meet global threshold 80%
229+
Error: Coverage for statements 75% does not meet global threshold 80%
230+
Error: Coverage for functions 75% does not meet global threshold 80%
231+
Error: Coverage for branches 75% does not meet global threshold 80%
232+
Error: Uncovered lines 20 exceeds maximum global threshold allowed 10
231233
```

0 commit comments

Comments
 (0)