@t09tanaka/test-gates 0.1.0 → 0.2.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/CHANGELOG.md +26 -0
- package/README.md +191 -19
- package/dist/{chunk-LMD3ANJ2.js → chunk-XEKQKLKY.js} +48 -4
- package/dist/cli.js +364 -133
- package/dist/index.cjs +225 -81
- package/dist/index.d.cts +40 -0
- package/dist/index.d.ts +40 -0
- package/dist/index.js +178 -78
- package/dist/jest.cjs +48 -4
- package/dist/jest.js +1 -1
- package/dist/stryker.cjs +48 -4
- package/dist/stryker.js +1 -1
- package/dist/vitest.cjs +50 -5
- package/dist/vitest.d.cts +6 -0
- package/dist/vitest.d.ts +6 -0
- package/dist/vitest.js +3 -2
- package/package.json +9 -3
- package/schema/test-gates.schema.json +164 -27
package/dist/vitest.d.cts
CHANGED
|
@@ -5,6 +5,12 @@ interface VitestGatesOptions {
|
|
|
5
5
|
test?: Record<string, unknown> & {
|
|
6
6
|
coverage?: Record<string, unknown>;
|
|
7
7
|
};
|
|
8
|
+
/**
|
|
9
|
+
* Coverage provider. Default `v8`. `istanbul` (needs `@vitest/coverage-istanbul`) also counts
|
|
10
|
+
* the skipped side of an `if` without `else` on Vitest 3, and an unused default parameter on
|
|
11
|
+
* every version; see the README for the measurements. Same as `test.coverage.provider`.
|
|
12
|
+
*/
|
|
13
|
+
coverageProvider?: 'v8' | 'istanbul';
|
|
8
14
|
/** Any other Vite option: resolve.alias, esbuild, plugins, … */
|
|
9
15
|
[option: string]: unknown;
|
|
10
16
|
}
|
package/dist/vitest.d.ts
CHANGED
|
@@ -5,6 +5,12 @@ interface VitestGatesOptions {
|
|
|
5
5
|
test?: Record<string, unknown> & {
|
|
6
6
|
coverage?: Record<string, unknown>;
|
|
7
7
|
};
|
|
8
|
+
/**
|
|
9
|
+
* Coverage provider. Default `v8`. `istanbul` (needs `@vitest/coverage-istanbul`) also counts
|
|
10
|
+
* the skipped side of an `if` without `else` on Vitest 3, and an unused default parameter on
|
|
11
|
+
* every version; see the README for the measurements. Same as `test.coverage.provider`.
|
|
12
|
+
*/
|
|
13
|
+
coverageProvider?: 'v8' | 'istanbul';
|
|
8
14
|
/** Any other Vite option: resolve.alias, esbuild, plugins, … */
|
|
9
15
|
[option: string]: unknown;
|
|
10
16
|
}
|
package/dist/vitest.js
CHANGED
|
@@ -6,11 +6,11 @@ import {
|
|
|
6
6
|
NO_SPEC_PATTERN,
|
|
7
7
|
gateSpecs,
|
|
8
8
|
loadGates
|
|
9
|
-
} from "./chunk-
|
|
9
|
+
} from "./chunk-XEKQKLKY.js";
|
|
10
10
|
|
|
11
11
|
// src/vitest.ts
|
|
12
12
|
function createVitestGatesConfig(options = {}) {
|
|
13
|
-
const { rootDir: rootDirOption, test = {}, ...overrides } = options;
|
|
13
|
+
const { rootDir: rootDirOption, coverageProvider, test = {}, ...overrides } = options;
|
|
14
14
|
const rootDir = resolveRootDir(rootDirOption);
|
|
15
15
|
const manifest = loadGates(rootDir);
|
|
16
16
|
const { coverage = {}, ...testOverrides } = test;
|
|
@@ -30,6 +30,7 @@ function createVitestGatesConfig(options = {}) {
|
|
|
30
30
|
reporter: ["text"],
|
|
31
31
|
reportsDirectory: "coverage/gates",
|
|
32
32
|
...coverage,
|
|
33
|
+
...coverageProvider === void 0 ? {} : { provider: coverageProvider },
|
|
33
34
|
enabled: true,
|
|
34
35
|
include: manifest.gates.map((gate) => gate.path),
|
|
35
36
|
thresholds: { perFile: true, ...FULL_COVERAGE }
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@t09tanaka/test-gates",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "Test gates for the code that must not be wrong: per-file 100% coverage as the floor, mutation testing with a per-mutant allow list as the real check.",
|
|
5
5
|
"author": "t09tanaka",
|
|
6
6
|
"license": "MIT",
|
|
@@ -76,7 +76,8 @@
|
|
|
76
76
|
},
|
|
77
77
|
"files": [
|
|
78
78
|
"dist",
|
|
79
|
-
"schema"
|
|
79
|
+
"schema",
|
|
80
|
+
"CHANGELOG.md"
|
|
80
81
|
],
|
|
81
82
|
"engines": {
|
|
82
83
|
"node": ">=20"
|
|
@@ -103,7 +104,8 @@
|
|
|
103
104
|
"peerDependencies": {
|
|
104
105
|
"@stryker-mutator/core": ">=9",
|
|
105
106
|
"jest": ">=29",
|
|
106
|
-
"vitest": ">=3"
|
|
107
|
+
"vitest": ">=3",
|
|
108
|
+
"@vitest/coverage-istanbul": ">=3"
|
|
107
109
|
},
|
|
108
110
|
"peerDependenciesMeta": {
|
|
109
111
|
"@stryker-mutator/core": {
|
|
@@ -114,6 +116,9 @@
|
|
|
114
116
|
},
|
|
115
117
|
"vitest": {
|
|
116
118
|
"optional": true
|
|
119
|
+
},
|
|
120
|
+
"@vitest/coverage-istanbul": {
|
|
121
|
+
"optional": true
|
|
117
122
|
}
|
|
118
123
|
},
|
|
119
124
|
"devDependencies": {
|
|
@@ -121,6 +126,7 @@
|
|
|
121
126
|
"@stryker-mutator/core": "^10.0.0",
|
|
122
127
|
"@stryker-mutator/vitest-runner": "^10.0.0",
|
|
123
128
|
"@types/node": "^22.20.5",
|
|
129
|
+
"@vitest/coverage-istanbul": "^3.2.7",
|
|
124
130
|
"@vitest/coverage-v8": "^3.2.7",
|
|
125
131
|
"eslint": "^9.39.5",
|
|
126
132
|
"eslint-config-prettier": "^10.1.8",
|
|
@@ -7,22 +7,34 @@
|
|
|
7
7
|
"required": ["gates"],
|
|
8
8
|
"additionalProperties": false,
|
|
9
9
|
"properties": {
|
|
10
|
-
"$schema": {
|
|
10
|
+
"$schema": {
|
|
11
|
+
"type": "string"
|
|
12
|
+
},
|
|
11
13
|
"gates": {
|
|
12
14
|
"description": "Pure modules that hold an important decision. Each must reach 100% on all four coverage metrics and leave no surviving mutant outside its allow list.",
|
|
13
15
|
"type": "array",
|
|
14
16
|
"minItems": 1,
|
|
15
|
-
"items": {
|
|
17
|
+
"items": {
|
|
18
|
+
"$ref": "#/definitions/gate"
|
|
19
|
+
}
|
|
16
20
|
},
|
|
17
21
|
"candidates": {
|
|
18
22
|
"description": "Important logic that cannot be a gate yet. Nothing is enforced for these beyond their existence.",
|
|
19
23
|
"type": "array",
|
|
20
|
-
"items": {
|
|
24
|
+
"items": {
|
|
25
|
+
"$ref": "#/definitions/candidate"
|
|
26
|
+
}
|
|
21
27
|
},
|
|
22
|
-
"settings": {
|
|
28
|
+
"settings": {
|
|
29
|
+
"$ref": "#/definitions/settings"
|
|
30
|
+
}
|
|
23
31
|
},
|
|
24
32
|
"definitions": {
|
|
25
|
-
"nonEmptyString": {
|
|
33
|
+
"nonEmptyString": {
|
|
34
|
+
"type": "string",
|
|
35
|
+
"minLength": 1,
|
|
36
|
+
"pattern": "\\S"
|
|
37
|
+
},
|
|
26
38
|
"gate": {
|
|
27
39
|
"type": "object",
|
|
28
40
|
"required": ["path", "decides", "impact"],
|
|
@@ -43,7 +55,16 @@
|
|
|
43
55
|
"equivalentMutants": {
|
|
44
56
|
"description": "Surviving mutants that are allowed because they cannot be observed from outside. One entry allows exactly one mutant.",
|
|
45
57
|
"type": "array",
|
|
46
|
-
"items": {
|
|
58
|
+
"items": {
|
|
59
|
+
"$ref": "#/definitions/equivalentMutant"
|
|
60
|
+
}
|
|
61
|
+
},
|
|
62
|
+
"expectedTimeouts": {
|
|
63
|
+
"description": "Since 0.2.0. Mutants that can never finish (a loop that no longer ends) and therefore always time out. Same key as equivalentMutants. They are not counted against settings.mutation.maxTimeouts. An entry whose mutant survives, or does not exist, fails the run; one that is killed is accepted.",
|
|
64
|
+
"type": "array",
|
|
65
|
+
"items": {
|
|
66
|
+
"$ref": "#/definitions/equivalentMutant"
|
|
67
|
+
}
|
|
47
68
|
}
|
|
48
69
|
}
|
|
49
70
|
},
|
|
@@ -84,7 +105,9 @@
|
|
|
84
105
|
"description": "File or directory, relative to the subproject.",
|
|
85
106
|
"$ref": "#/definitions/nonEmptyString"
|
|
86
107
|
},
|
|
87
|
-
"decides": {
|
|
108
|
+
"decides": {
|
|
109
|
+
"$ref": "#/definitions/nonEmptyString"
|
|
110
|
+
},
|
|
88
111
|
"blocker": {
|
|
89
112
|
"description": "What has to happen before this can become a gate.",
|
|
90
113
|
"$ref": "#/definitions/nonEmptyString"
|
|
@@ -94,15 +117,23 @@
|
|
|
94
117
|
"patternRule": {
|
|
95
118
|
"description": "A regular expression, either as a string or with flags and a reason.",
|
|
96
119
|
"oneOf": [
|
|
97
|
-
{
|
|
120
|
+
{
|
|
121
|
+
"$ref": "#/definitions/nonEmptyString"
|
|
122
|
+
},
|
|
98
123
|
{
|
|
99
124
|
"type": "object",
|
|
100
125
|
"required": ["pattern"],
|
|
101
126
|
"additionalProperties": false,
|
|
102
127
|
"properties": {
|
|
103
|
-
"pattern": {
|
|
104
|
-
|
|
105
|
-
|
|
128
|
+
"pattern": {
|
|
129
|
+
"$ref": "#/definitions/nonEmptyString"
|
|
130
|
+
},
|
|
131
|
+
"flags": {
|
|
132
|
+
"type": "string"
|
|
133
|
+
},
|
|
134
|
+
"reason": {
|
|
135
|
+
"type": "string"
|
|
136
|
+
}
|
|
106
137
|
}
|
|
107
138
|
}
|
|
108
139
|
]
|
|
@@ -116,7 +147,12 @@
|
|
|
116
147
|
"type": "boolean",
|
|
117
148
|
"default": true
|
|
118
149
|
},
|
|
119
|
-
"add": {
|
|
150
|
+
"add": {
|
|
151
|
+
"type": "array",
|
|
152
|
+
"items": {
|
|
153
|
+
"$ref": "#/definitions/patternRule"
|
|
154
|
+
}
|
|
155
|
+
}
|
|
120
156
|
}
|
|
121
157
|
},
|
|
122
158
|
"settings": {
|
|
@@ -128,7 +164,10 @@
|
|
|
128
164
|
"description": "File extensions a gate may have.",
|
|
129
165
|
"type": "array",
|
|
130
166
|
"minItems": 1,
|
|
131
|
-
"items": {
|
|
167
|
+
"items": {
|
|
168
|
+
"type": "string",
|
|
169
|
+
"pattern": "^\\."
|
|
170
|
+
},
|
|
132
171
|
"default": [".ts", ".mts", ".cts", ".js", ".mjs", ".cjs"]
|
|
133
172
|
},
|
|
134
173
|
"spec": {
|
|
@@ -138,7 +177,9 @@
|
|
|
138
177
|
"suffixes": {
|
|
139
178
|
"description": "Replaces the gate's extension to form the spec path. Default: \".spec<ext>\" and \".test<ext>\". An empty array means the project has no spec convention.",
|
|
140
179
|
"type": "array",
|
|
141
|
-
"items": {
|
|
180
|
+
"items": {
|
|
181
|
+
"$ref": "#/definitions/nonEmptyString"
|
|
182
|
+
}
|
|
142
183
|
},
|
|
143
184
|
"rewrite": {
|
|
144
185
|
"description": "Regular expression replacements applied to the gate path before the suffix, for specs that live in another directory.",
|
|
@@ -148,8 +189,12 @@
|
|
|
148
189
|
"required": ["from", "to"],
|
|
149
190
|
"additionalProperties": false,
|
|
150
191
|
"properties": {
|
|
151
|
-
"from": {
|
|
152
|
-
|
|
192
|
+
"from": {
|
|
193
|
+
"$ref": "#/definitions/nonEmptyString"
|
|
194
|
+
},
|
|
195
|
+
"to": {
|
|
196
|
+
"type": "string"
|
|
197
|
+
}
|
|
153
198
|
}
|
|
154
199
|
}
|
|
155
200
|
}
|
|
@@ -160,12 +205,22 @@
|
|
|
160
205
|
"type": "object",
|
|
161
206
|
"additionalProperties": false,
|
|
162
207
|
"properties": {
|
|
163
|
-
"defaults": {
|
|
164
|
-
|
|
208
|
+
"defaults": {
|
|
209
|
+
"type": "boolean",
|
|
210
|
+
"default": true
|
|
211
|
+
},
|
|
212
|
+
"add": {
|
|
213
|
+
"type": "array",
|
|
214
|
+
"items": {
|
|
215
|
+
"$ref": "#/definitions/patternRule"
|
|
216
|
+
}
|
|
217
|
+
},
|
|
165
218
|
"allow": {
|
|
166
219
|
"description": "Module specifiers exempt from every rule (regular expressions).",
|
|
167
220
|
"type": "array",
|
|
168
|
-
"items": {
|
|
221
|
+
"items": {
|
|
222
|
+
"$ref": "#/definitions/nonEmptyString"
|
|
223
|
+
}
|
|
169
224
|
}
|
|
170
225
|
}
|
|
171
226
|
},
|
|
@@ -174,7 +229,10 @@
|
|
|
174
229
|
"type": "object",
|
|
175
230
|
"additionalProperties": false,
|
|
176
231
|
"properties": {
|
|
177
|
-
"defaults": {
|
|
232
|
+
"defaults": {
|
|
233
|
+
"type": "boolean",
|
|
234
|
+
"default": true
|
|
235
|
+
},
|
|
178
236
|
"add": {
|
|
179
237
|
"type": "array",
|
|
180
238
|
"items": {
|
|
@@ -182,13 +240,19 @@
|
|
|
182
240
|
"required": ["module", "names"],
|
|
183
241
|
"additionalProperties": false,
|
|
184
242
|
"properties": {
|
|
185
|
-
"module": {
|
|
243
|
+
"module": {
|
|
244
|
+
"$ref": "#/definitions/nonEmptyString"
|
|
245
|
+
},
|
|
186
246
|
"names": {
|
|
187
247
|
"type": "array",
|
|
188
248
|
"minItems": 1,
|
|
189
|
-
"items": {
|
|
249
|
+
"items": {
|
|
250
|
+
"$ref": "#/definitions/nonEmptyString"
|
|
251
|
+
}
|
|
190
252
|
},
|
|
191
|
-
"reason": {
|
|
253
|
+
"reason": {
|
|
254
|
+
"type": "string"
|
|
255
|
+
}
|
|
192
256
|
}
|
|
193
257
|
}
|
|
194
258
|
}
|
|
@@ -210,8 +274,12 @@
|
|
|
210
274
|
"required": ["prefix", "target"],
|
|
211
275
|
"additionalProperties": false,
|
|
212
276
|
"properties": {
|
|
213
|
-
"prefix": {
|
|
214
|
-
|
|
277
|
+
"prefix": {
|
|
278
|
+
"$ref": "#/definitions/nonEmptyString"
|
|
279
|
+
},
|
|
280
|
+
"target": {
|
|
281
|
+
"type": "string"
|
|
282
|
+
}
|
|
215
283
|
}
|
|
216
284
|
}
|
|
217
285
|
},
|
|
@@ -219,7 +287,9 @@
|
|
|
219
287
|
"description": "The command `test-gates selfcheck` runs. Default: `jest --config jest.gates.config.*` or `vitest run --config vitest.gates.config.*`, whichever config exists.",
|
|
220
288
|
"type": "array",
|
|
221
289
|
"minItems": 1,
|
|
222
|
-
"items": {
|
|
290
|
+
"items": {
|
|
291
|
+
"$ref": "#/definitions/nonEmptyString"
|
|
292
|
+
}
|
|
223
293
|
},
|
|
224
294
|
"selfcheck": {
|
|
225
295
|
"type": "object",
|
|
@@ -262,7 +332,74 @@
|
|
|
262
332
|
"summaryExclude": {
|
|
263
333
|
"description": "Paths left out of the reference total (regular expressions).",
|
|
264
334
|
"type": "array",
|
|
265
|
-
"items": {
|
|
335
|
+
"items": {
|
|
336
|
+
"$ref": "#/definitions/nonEmptyString"
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
},
|
|
341
|
+
"imports": {
|
|
342
|
+
"description": "Since 0.2.0. How runtime imports of a gate are judged.",
|
|
343
|
+
"type": "object",
|
|
344
|
+
"additionalProperties": false,
|
|
345
|
+
"properties": {
|
|
346
|
+
"mode": {
|
|
347
|
+
"description": "blocklist (default): only the impureImports rules apply. allowlist: in addition, a runtime import must be relative, go through an import alias, or be listed in allow; a project import must not point at a file matched by impurePaths; a computed require() / import() is rejected.",
|
|
348
|
+
"enum": ["blocklist", "allowlist"],
|
|
349
|
+
"default": "blocklist"
|
|
350
|
+
},
|
|
351
|
+
"allow": {
|
|
352
|
+
"description": "Packages a gate may import in allowlist mode. A string is a package name (its subpaths included). { pattern } is a regular expression tested against the specifier. { module, names } allows only those named imports.",
|
|
353
|
+
"type": "array",
|
|
354
|
+
"items": {
|
|
355
|
+
"oneOf": [
|
|
356
|
+
{
|
|
357
|
+
"$ref": "#/definitions/nonEmptyString"
|
|
358
|
+
},
|
|
359
|
+
{
|
|
360
|
+
"type": "object",
|
|
361
|
+
"required": ["pattern"],
|
|
362
|
+
"additionalProperties": false,
|
|
363
|
+
"properties": {
|
|
364
|
+
"pattern": {
|
|
365
|
+
"$ref": "#/definitions/nonEmptyString"
|
|
366
|
+
},
|
|
367
|
+
"flags": {
|
|
368
|
+
"type": "string"
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
},
|
|
372
|
+
{
|
|
373
|
+
"type": "object",
|
|
374
|
+
"required": ["module", "names"],
|
|
375
|
+
"additionalProperties": false,
|
|
376
|
+
"properties": {
|
|
377
|
+
"module": {
|
|
378
|
+
"$ref": "#/definitions/nonEmptyString"
|
|
379
|
+
},
|
|
380
|
+
"names": {
|
|
381
|
+
"type": "array",
|
|
382
|
+
"minItems": 1,
|
|
383
|
+
"items": {
|
|
384
|
+
"$ref": "#/definitions/nonEmptyString"
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
]
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
},
|
|
394
|
+
"mutation": {
|
|
395
|
+
"description": "Since 0.2.0.",
|
|
396
|
+
"type": "object",
|
|
397
|
+
"additionalProperties": false,
|
|
398
|
+
"properties": {
|
|
399
|
+
"maxTimeouts": {
|
|
400
|
+
"description": "How many timed-out mutants outside expectedTimeouts are tolerated. Above it the run fails. Not set: no limit.",
|
|
401
|
+
"type": "integer",
|
|
402
|
+
"minimum": 0
|
|
266
403
|
}
|
|
267
404
|
}
|
|
268
405
|
}
|