@t09tanaka/test-gates 0.3.3 → 0.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/CHANGELOG.md +44 -0
- package/README.md +290 -17
- package/dist/{chunk-F7WBEFIL.js → chunk-AK2NTFQO.js} +39 -10
- package/dist/chunk-TOKHAUFX.js +163 -0
- package/dist/cli.js +3129 -501
- package/dist/index.cjs +37 -12
- package/dist/index.d.cts +10 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.js +4 -3
- package/dist/jest-touch-reporter.cjs +129 -0
- package/dist/jest-touch-reporter.d.cts +19 -0
- package/dist/jest-touch-reporter.d.ts +19 -0
- package/dist/jest-touch-reporter.js +48 -0
- package/dist/jest.cjs +70 -12
- package/dist/jest.js +19 -2
- package/dist/stryker.cjs +29 -10
- package/dist/stryker.d.cts +15 -1
- package/dist/stryker.d.ts +15 -1
- package/dist/stryker.js +10 -3
- package/dist/vitest.cjs +151 -12
- package/dist/vitest.js +22 -4
- package/package.json +7 -2
- /package/dist/{chunk-4HAJIKDZ.js → chunk-C4R3RDHF.js} +0 -0
package/dist/stryker.d.cts
CHANGED
|
@@ -12,13 +12,27 @@ interface StrykerGatesOptions {
|
|
|
12
12
|
* mutants the speed stayed the same to the end.
|
|
13
13
|
*/
|
|
14
14
|
declare const DEFAULT_MAX_TEST_RUNNER_REUSE = 50;
|
|
15
|
+
/**
|
|
16
|
+
* How many test runners Stryker starts when neither the config nor the command line says
|
|
17
|
+
* (`concurrency`). Stryker's own default is one fewer than the cores (on more than four), but
|
|
18
|
+
* each of its runners is a Jest or Vitest that does work besides the tests (transforms, coverage),
|
|
19
|
+
* and the machine is rarely free for the run alone: half of the cores, at most 8. Measured on
|
|
20
|
+
* two projects on a machine with 12 cores, 6 runners took about half the time of 2, with the
|
|
21
|
+
* same numbers of killed, survived and timed-out mutants. In CI (`CI` set) 2, the cores of the
|
|
22
|
+
* usual hosted runner: more runners than cores only make the mutants run out of time.
|
|
23
|
+
*
|
|
24
|
+
* @param parallelism What `os.availableParallelism()` says.
|
|
25
|
+
*/
|
|
26
|
+
declare function defaultConcurrency(parallelism: number, ci: boolean): number;
|
|
15
27
|
/**
|
|
16
28
|
* Stryker config for the gates: mutate exactly the gates, never fail on the score
|
|
17
29
|
* (`thresholds.break: null`), and write the JSON report that `test-gates mutation` judges.
|
|
18
30
|
*
|
|
19
31
|
* `mutate`, `thresholds.break` and `jsonReporter.fileName` are always set here, and `json` is
|
|
20
32
|
* always among the reporters. Options that take mutants out of the evaluation are rejected.
|
|
33
|
+
* `concurrency` is defaultConcurrency unless the options set it; `--concurrency` on Stryker's
|
|
34
|
+
* command line wins over both.
|
|
21
35
|
*/
|
|
22
36
|
declare function createStrykerGatesConfig(options?: StrykerGatesOptions): Record<string, unknown>;
|
|
23
37
|
|
|
24
|
-
export { DEFAULT_MAX_TEST_RUNNER_REUSE, type StrykerGatesOptions, createStrykerGatesConfig };
|
|
38
|
+
export { DEFAULT_MAX_TEST_RUNNER_REUSE, type StrykerGatesOptions, createStrykerGatesConfig, defaultConcurrency };
|
package/dist/stryker.d.ts
CHANGED
|
@@ -12,13 +12,27 @@ interface StrykerGatesOptions {
|
|
|
12
12
|
* mutants the speed stayed the same to the end.
|
|
13
13
|
*/
|
|
14
14
|
declare const DEFAULT_MAX_TEST_RUNNER_REUSE = 50;
|
|
15
|
+
/**
|
|
16
|
+
* How many test runners Stryker starts when neither the config nor the command line says
|
|
17
|
+
* (`concurrency`). Stryker's own default is one fewer than the cores (on more than four), but
|
|
18
|
+
* each of its runners is a Jest or Vitest that does work besides the tests (transforms, coverage),
|
|
19
|
+
* and the machine is rarely free for the run alone: half of the cores, at most 8. Measured on
|
|
20
|
+
* two projects on a machine with 12 cores, 6 runners took about half the time of 2, with the
|
|
21
|
+
* same numbers of killed, survived and timed-out mutants. In CI (`CI` set) 2, the cores of the
|
|
22
|
+
* usual hosted runner: more runners than cores only make the mutants run out of time.
|
|
23
|
+
*
|
|
24
|
+
* @param parallelism What `os.availableParallelism()` says.
|
|
25
|
+
*/
|
|
26
|
+
declare function defaultConcurrency(parallelism: number, ci: boolean): number;
|
|
15
27
|
/**
|
|
16
28
|
* Stryker config for the gates: mutate exactly the gates, never fail on the score
|
|
17
29
|
* (`thresholds.break: null`), and write the JSON report that `test-gates mutation` judges.
|
|
18
30
|
*
|
|
19
31
|
* `mutate`, `thresholds.break` and `jsonReporter.fileName` are always set here, and `json` is
|
|
20
32
|
* always among the reporters. Options that take mutants out of the evaluation are rejected.
|
|
33
|
+
* `concurrency` is defaultConcurrency unless the options set it; `--concurrency` on Stryker's
|
|
34
|
+
* command line wins over both.
|
|
21
35
|
*/
|
|
22
36
|
declare function createStrykerGatesConfig(options?: StrykerGatesOptions): Record<string, unknown>;
|
|
23
37
|
|
|
24
|
-
export { DEFAULT_MAX_TEST_RUNNER_REUSE, type StrykerGatesOptions, createStrykerGatesConfig };
|
|
38
|
+
export { DEFAULT_MAX_TEST_RUNNER_REUSE, type StrykerGatesOptions, createStrykerGatesConfig, defaultConcurrency };
|
package/dist/stryker.js
CHANGED
|
@@ -1,13 +1,18 @@
|
|
|
1
1
|
import {
|
|
2
2
|
resolveRootDir
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-C4R3RDHF.js";
|
|
4
4
|
import {
|
|
5
|
+
inCi,
|
|
5
6
|
loadGates
|
|
6
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-AK2NTFQO.js";
|
|
7
8
|
|
|
8
9
|
// src/stryker.ts
|
|
10
|
+
import os from "os";
|
|
9
11
|
var WEAKENING_OPTIONS = ["ignoreStatic", "ignorers"];
|
|
10
12
|
var DEFAULT_MAX_TEST_RUNNER_REUSE = 50;
|
|
13
|
+
function defaultConcurrency(parallelism, ci) {
|
|
14
|
+
return ci ? 2 : Math.min(8, Math.max(1, Math.floor(parallelism / 2)));
|
|
15
|
+
}
|
|
11
16
|
function createStrykerGatesConfig(options = {}) {
|
|
12
17
|
const { rootDir: rootDirOption, ...overrides } = options;
|
|
13
18
|
for (const option of WEAKENING_OPTIONS) {
|
|
@@ -33,6 +38,7 @@ function createStrykerGatesConfig(options = {}) {
|
|
|
33
38
|
incrementalFile: "reports/stryker-incremental.json",
|
|
34
39
|
tempDirName: ".stryker-tmp",
|
|
35
40
|
maxTestRunnerReuse: DEFAULT_MAX_TEST_RUNNER_REUSE,
|
|
41
|
+
concurrency: defaultConcurrency(os.availableParallelism(), inCi(process.env)),
|
|
36
42
|
clearTextReporter: { logTests: false, reportTests: false },
|
|
37
43
|
...overrides,
|
|
38
44
|
mutate: manifest.gates.map((gate) => gate.path),
|
|
@@ -43,5 +49,6 @@ function createStrykerGatesConfig(options = {}) {
|
|
|
43
49
|
}
|
|
44
50
|
export {
|
|
45
51
|
DEFAULT_MAX_TEST_RUNNER_REUSE,
|
|
46
|
-
createStrykerGatesConfig
|
|
52
|
+
createStrykerGatesConfig,
|
|
53
|
+
defaultConcurrency
|
|
47
54
|
};
|
package/dist/vitest.cjs
CHANGED
|
@@ -33,6 +33,7 @@ __export(vitest_exports, {
|
|
|
33
33
|
createVitestGatesConfig: () => createVitestGatesConfig
|
|
34
34
|
});
|
|
35
35
|
module.exports = __toCommonJS(vitest_exports);
|
|
36
|
+
var import_node_path5 = __toESM(require("path"), 1);
|
|
36
37
|
|
|
37
38
|
// src/load.ts
|
|
38
39
|
var import_node_fs2 = __toESM(require("fs"), 1);
|
|
@@ -489,6 +490,24 @@ function resolveSettings(raw) {
|
|
|
489
490
|
|
|
490
491
|
// src/core/selfcheck.ts
|
|
491
492
|
var EXCLUDE_ENV = "TEST_GATES_EXCLUDE_SPEC";
|
|
493
|
+
var EXCLUDE_ALL_ENV = "TEST_GATES_EXCLUDE_SPECS";
|
|
494
|
+
function excludedSpecs(env) {
|
|
495
|
+
const one = env[EXCLUDE_ENV];
|
|
496
|
+
const several = env[EXCLUDE_ALL_ENV];
|
|
497
|
+
const excluded = one === void 0 || one === "" ? [] : [one];
|
|
498
|
+
if (several === void 0 || several === "") {
|
|
499
|
+
return excluded;
|
|
500
|
+
}
|
|
501
|
+
let listed = null;
|
|
502
|
+
try {
|
|
503
|
+
listed = JSON.parse(several);
|
|
504
|
+
} catch {
|
|
505
|
+
}
|
|
506
|
+
if (!Array.isArray(listed) || !listed.every((spec) => typeof spec === "string")) {
|
|
507
|
+
throw new Error(`test-gates: ${EXCLUDE_ALL_ENV} must be a JSON list of spec paths`);
|
|
508
|
+
}
|
|
509
|
+
return [...excluded, ...listed];
|
|
510
|
+
}
|
|
492
511
|
|
|
493
512
|
// src/core/spec-path.ts
|
|
494
513
|
function gateExtensionOf(gatePath, gateExtensions) {
|
|
@@ -513,19 +532,22 @@ function specCandidatesFor(gatePath, settings) {
|
|
|
513
532
|
// src/fs.ts
|
|
514
533
|
var import_node_fs = __toESM(require("fs"), 1);
|
|
515
534
|
var import_node_path = __toESM(require("path"), 1);
|
|
516
|
-
function existsExact(rootDir, relativePath, kind = "file") {
|
|
535
|
+
function existsExact(rootDir, relativePath, kind = "file", listing = /* @__PURE__ */ new Map()) {
|
|
517
536
|
let current = rootDir;
|
|
518
537
|
for (const segment of relativePath.split("/")) {
|
|
519
538
|
if (segment === "" || segment === ".") {
|
|
520
539
|
continue;
|
|
521
540
|
}
|
|
522
|
-
let entries;
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
541
|
+
let entries = listing.get(current);
|
|
542
|
+
if (entries === void 0) {
|
|
543
|
+
try {
|
|
544
|
+
entries = import_node_fs.default.readdirSync(current);
|
|
545
|
+
} catch {
|
|
546
|
+
entries = null;
|
|
547
|
+
}
|
|
548
|
+
listing.set(current, entries);
|
|
527
549
|
}
|
|
528
|
-
if (!entries.includes(segment)) {
|
|
550
|
+
if (entries === null || !entries.includes(segment)) {
|
|
529
551
|
return false;
|
|
530
552
|
}
|
|
531
553
|
current = import_node_path.default.join(current, segment);
|
|
@@ -560,9 +582,12 @@ function readManifest(dir) {
|
|
|
560
582
|
}
|
|
561
583
|
throw error;
|
|
562
584
|
}
|
|
585
|
+
const listing = /* @__PURE__ */ new Map();
|
|
563
586
|
const gates = parsed.gates.map((gate) => {
|
|
564
587
|
const specCandidates = specCandidatesFor(gate.path, settings);
|
|
565
|
-
const existingSpecs = specCandidates.filter(
|
|
588
|
+
const existingSpecs = specCandidates.filter(
|
|
589
|
+
(candidate) => existsExact(absoluteDir, candidate, "file", listing)
|
|
590
|
+
);
|
|
566
591
|
return {
|
|
567
592
|
...gate,
|
|
568
593
|
specCandidates,
|
|
@@ -589,8 +614,8 @@ function loadGates(dir = process.cwd()) {
|
|
|
589
614
|
return manifest;
|
|
590
615
|
}
|
|
591
616
|
function gateSpecs(manifest, env = process.env) {
|
|
592
|
-
const excluded = env
|
|
593
|
-
return manifest.gates.map((gate) => gate.spec).filter((spec) => spec !== null && spec
|
|
617
|
+
const excluded = excludedSpecs(env);
|
|
618
|
+
return manifest.gates.map((gate) => gate.spec).filter((spec) => spec !== null && !excluded.includes(spec));
|
|
594
619
|
}
|
|
595
620
|
var NO_SPEC_PATTERN = "__test_gates_no_spec_left__";
|
|
596
621
|
|
|
@@ -608,6 +633,107 @@ function resolveRootDir(rootDir) {
|
|
|
608
633
|
return import_node_path3.default.resolve(rootDir);
|
|
609
634
|
}
|
|
610
635
|
|
|
636
|
+
// src/touch.ts
|
|
637
|
+
var import_node_crypto = require("crypto");
|
|
638
|
+
var import_node_fs3 = __toESM(require("fs"), 1);
|
|
639
|
+
var import_node_path4 = __toESM(require("path"), 1);
|
|
640
|
+
var import_node_url2 = require("url");
|
|
641
|
+
var TOUCH_ENV = "TEST_GATES_TOUCH_DIR";
|
|
642
|
+
var VITEST_PROVIDER = `import { createHash } from 'node:crypto';
|
|
643
|
+
import fs from 'node:fs';
|
|
644
|
+
import path from 'node:path';
|
|
645
|
+
import istanbul from '@vitest/coverage-istanbul';
|
|
646
|
+
|
|
647
|
+
const directory = "__DIRECTORY__";
|
|
648
|
+
const run = { runner: 'vitest', version: 'unknown', provider: 'istanbul', pool: 'unknown', isolate: false, projects: 0, problem: null };
|
|
649
|
+
const save = () => fs.writeFileSync(path.join(directory, 'run.json'), JSON.stringify(run));
|
|
650
|
+
const problem = (text) => {
|
|
651
|
+
run.problem ??= text;
|
|
652
|
+
save();
|
|
653
|
+
};
|
|
654
|
+
|
|
655
|
+
const counts = (coverage) =>
|
|
656
|
+
Object.fromEntries(
|
|
657
|
+
Object.entries(coverage).map(([file, counters]) => [
|
|
658
|
+
file,
|
|
659
|
+
{
|
|
660
|
+
s: Object.values(counters.s ?? {}),
|
|
661
|
+
f: Object.values(counters.f ?? {}),
|
|
662
|
+
b: Object.values(counters.b ?? {}).flat(),
|
|
663
|
+
},
|
|
664
|
+
])
|
|
665
|
+
);
|
|
666
|
+
const add = (before, now) =>
|
|
667
|
+
before.length === now.length ? before.map((count, index) => count + now[index]) : [-1];
|
|
668
|
+
|
|
669
|
+
function note(spec, files) {
|
|
670
|
+
const file = path.join(directory, 'specs', createHash('sha256').update(spec).digest('hex') + '.json');
|
|
671
|
+
let before = {};
|
|
672
|
+
try {
|
|
673
|
+
before = JSON.parse(fs.readFileSync(file, 'utf8')).files;
|
|
674
|
+
} catch {
|
|
675
|
+
// the first note of this spec
|
|
676
|
+
}
|
|
677
|
+
const merged = { ...files };
|
|
678
|
+
for (const [name, counters] of Object.entries(before)) {
|
|
679
|
+
const now = files[name];
|
|
680
|
+
merged[name] =
|
|
681
|
+
now === undefined
|
|
682
|
+
? counters
|
|
683
|
+
: { s: add(counters.s, now.s), f: add(counters.f, now.f), b: add(counters.b, now.b) };
|
|
684
|
+
}
|
|
685
|
+
fs.writeFileSync(file, JSON.stringify({ spec, files: merged }));
|
|
686
|
+
}
|
|
687
|
+
|
|
688
|
+
export default {
|
|
689
|
+
...istanbul,
|
|
690
|
+
async getProvider() {
|
|
691
|
+
const provider = await istanbul.getProvider();
|
|
692
|
+
fs.mkdirSync(path.join(directory, 'specs'), { recursive: true });
|
|
693
|
+
save();
|
|
694
|
+
if (typeof provider.onAfterSuiteRun !== 'function' || typeof provider.initialize !== 'function') {
|
|
695
|
+
problem('the coverage provider of this Vitest has no onAfterSuiteRun or no initialize');
|
|
696
|
+
return provider;
|
|
697
|
+
}
|
|
698
|
+
const initialize = provider.initialize.bind(provider);
|
|
699
|
+
provider.initialize = (ctx) => {
|
|
700
|
+
// A project can set its own pool and isolation: all of them have to say the same.
|
|
701
|
+
const projects = Array.isArray(ctx?.projects) ? ctx.projects : [];
|
|
702
|
+
const configs = [ctx?.config ?? {}, ...projects.map((project) => project?.config ?? {})];
|
|
703
|
+
const pools = [...new Set(configs.map((config) => (typeof config.pool === 'string' ? config.pool : 'unknown')))];
|
|
704
|
+
run.version = typeof ctx?.version === 'string' ? ctx.version : 'unknown';
|
|
705
|
+
run.pool = pools.length === 1 ? pools[0] : 'mixed';
|
|
706
|
+
run.isolate = configs.every(
|
|
707
|
+
(config) => (config.poolOptions?.[config.pool]?.isolate ?? config.isolate) === true
|
|
708
|
+
);
|
|
709
|
+
run.projects = projects.length;
|
|
710
|
+
save();
|
|
711
|
+
return initialize(ctx);
|
|
712
|
+
};
|
|
713
|
+
const handOver = provider.onAfterSuiteRun.bind(provider);
|
|
714
|
+
provider.onAfterSuiteRun = (meta) => {
|
|
715
|
+
const coverage = meta?.coverage;
|
|
716
|
+
if (coverage !== undefined && coverage !== null) {
|
|
717
|
+
const specs = meta.testFiles;
|
|
718
|
+
if (typeof coverage !== 'object' || !Array.isArray(specs) || specs.length !== 1 || typeof specs[0] !== 'string') {
|
|
719
|
+
problem('Vitest handed over coverage that is not that of exactly one spec file');
|
|
720
|
+
} else {
|
|
721
|
+
note(specs[0], counts(coverage));
|
|
722
|
+
}
|
|
723
|
+
}
|
|
724
|
+
return handOver(meta);
|
|
725
|
+
};
|
|
726
|
+
return provider;
|
|
727
|
+
},
|
|
728
|
+
};
|
|
729
|
+
`;
|
|
730
|
+
function vitestProviderFile(directory) {
|
|
731
|
+
const file = import_node_path4.default.join(directory, "vitest-provider.mjs");
|
|
732
|
+
import_node_fs3.default.mkdirSync(directory, { recursive: true });
|
|
733
|
+
import_node_fs3.default.writeFileSync(file, VITEST_PROVIDER.replace('"__DIRECTORY__"', JSON.stringify(directory)));
|
|
734
|
+
return file;
|
|
735
|
+
}
|
|
736
|
+
|
|
611
737
|
// src/vitest.ts
|
|
612
738
|
function createVitestGatesConfig(options = {}) {
|
|
613
739
|
const { rootDir: rootDirOption, coverageProvider, test = {}, ...overrides } = options;
|
|
@@ -615,6 +741,8 @@ function createVitestGatesConfig(options = {}) {
|
|
|
615
741
|
const manifest = loadGates(rootDir);
|
|
616
742
|
const { coverage = {}, ...testOverrides } = test;
|
|
617
743
|
const specs = gateSpecs(manifest);
|
|
744
|
+
const provider = coverageProvider ?? coverage.provider ?? "v8";
|
|
745
|
+
const touchDir = process.env[TOUCH_ENV];
|
|
618
746
|
return {
|
|
619
747
|
...overrides,
|
|
620
748
|
root: rootDir,
|
|
@@ -626,11 +754,22 @@ function createVitestGatesConfig(options = {}) {
|
|
|
626
754
|
// then fail on the coverage threshold, not on "no test files found".
|
|
627
755
|
passWithNoTests: true,
|
|
628
756
|
coverage: {
|
|
629
|
-
provider: "v8",
|
|
630
757
|
reporter: ["text"],
|
|
631
758
|
reportsDirectory: "coverage/gates",
|
|
632
759
|
...coverage,
|
|
633
|
-
|
|
760
|
+
provider,
|
|
761
|
+
// While `test-gates selfcheck` asks for it: write down what each spec executed. Only
|
|
762
|
+
// the istanbul provider can be wrapped for that (see src/touch.ts).
|
|
763
|
+
...touchDir && provider === "istanbul" ? {
|
|
764
|
+
provider: "custom",
|
|
765
|
+
customProviderModule: vitestProviderFile(touchDir),
|
|
766
|
+
// The runner's own total, to check the notes against.
|
|
767
|
+
reporter: [
|
|
768
|
+
...Array.isArray(coverage.reporter) ? coverage.reporter : [coverage.reporter ?? "text"],
|
|
769
|
+
"json"
|
|
770
|
+
],
|
|
771
|
+
reportsDirectory: import_node_path5.default.join(touchDir, "coverage")
|
|
772
|
+
} : {},
|
|
634
773
|
enabled: true,
|
|
635
774
|
include: manifest.gates.map((gate) => gate.path),
|
|
636
775
|
thresholds: { perFile: true, ...FULL_COVERAGE }
|
package/dist/vitest.js
CHANGED
|
@@ -1,20 +1,27 @@
|
|
|
1
|
+
import {
|
|
2
|
+
TOUCH_ENV,
|
|
3
|
+
vitestProviderFile
|
|
4
|
+
} from "./chunk-TOKHAUFX.js";
|
|
1
5
|
import {
|
|
2
6
|
FULL_COVERAGE,
|
|
3
7
|
resolveRootDir
|
|
4
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-C4R3RDHF.js";
|
|
5
9
|
import {
|
|
6
10
|
NO_SPEC_PATTERN,
|
|
7
11
|
gateSpecs,
|
|
8
12
|
loadGates
|
|
9
|
-
} from "./chunk-
|
|
13
|
+
} from "./chunk-AK2NTFQO.js";
|
|
10
14
|
|
|
11
15
|
// src/vitest.ts
|
|
16
|
+
import path from "path";
|
|
12
17
|
function createVitestGatesConfig(options = {}) {
|
|
13
18
|
const { rootDir: rootDirOption, coverageProvider, test = {}, ...overrides } = options;
|
|
14
19
|
const rootDir = resolveRootDir(rootDirOption);
|
|
15
20
|
const manifest = loadGates(rootDir);
|
|
16
21
|
const { coverage = {}, ...testOverrides } = test;
|
|
17
22
|
const specs = gateSpecs(manifest);
|
|
23
|
+
const provider = coverageProvider ?? coverage.provider ?? "v8";
|
|
24
|
+
const touchDir = process.env[TOUCH_ENV];
|
|
18
25
|
return {
|
|
19
26
|
...overrides,
|
|
20
27
|
root: rootDir,
|
|
@@ -26,11 +33,22 @@ function createVitestGatesConfig(options = {}) {
|
|
|
26
33
|
// then fail on the coverage threshold, not on "no test files found".
|
|
27
34
|
passWithNoTests: true,
|
|
28
35
|
coverage: {
|
|
29
|
-
provider: "v8",
|
|
30
36
|
reporter: ["text"],
|
|
31
37
|
reportsDirectory: "coverage/gates",
|
|
32
38
|
...coverage,
|
|
33
|
-
|
|
39
|
+
provider,
|
|
40
|
+
// While `test-gates selfcheck` asks for it: write down what each spec executed. Only
|
|
41
|
+
// the istanbul provider can be wrapped for that (see src/touch.ts).
|
|
42
|
+
...touchDir && provider === "istanbul" ? {
|
|
43
|
+
provider: "custom",
|
|
44
|
+
customProviderModule: vitestProviderFile(touchDir),
|
|
45
|
+
// The runner's own total, to check the notes against.
|
|
46
|
+
reporter: [
|
|
47
|
+
...Array.isArray(coverage.reporter) ? coverage.reporter : [coverage.reporter ?? "text"],
|
|
48
|
+
"json"
|
|
49
|
+
],
|
|
50
|
+
reportsDirectory: path.join(touchDir, "coverage")
|
|
51
|
+
} : {},
|
|
34
52
|
enabled: true,
|
|
35
53
|
include: manifest.gates.map((gate) => gate.path),
|
|
36
54
|
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.4.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",
|
|
@@ -94,12 +94,15 @@
|
|
|
94
94
|
"format:check": "prettier --check .",
|
|
95
95
|
"pretest": "npm run build",
|
|
96
96
|
"test": "vitest run",
|
|
97
|
+
"pretest:heavy": "npm run build",
|
|
98
|
+
"test:heavy": "vitest run --config vitest.heavy.config.ts",
|
|
99
|
+
"check:quick": "npm run lint && npm run typecheck && npm run format:check && npm test",
|
|
97
100
|
"test:watch": "vitest",
|
|
98
101
|
"test:coverage": "vitest run --coverage",
|
|
99
102
|
"test:gates": "node dist/cli.js check && vitest run --config vitest.gates.config.ts",
|
|
100
103
|
"test:gates:selfcheck": "node dist/cli.js selfcheck",
|
|
101
104
|
"test:gates:mutation": "node dist/cli.js mutation",
|
|
102
|
-
"prepublishOnly": "npm run lint && npm run format:check && npm run typecheck && npm test && npm run test:gates && npm run test:gates:selfcheck"
|
|
105
|
+
"prepublishOnly": "npm run lint && npm run format:check && npm run typecheck && npm test && npm run test:heavy && npm run test:gates && npm run test:gates:selfcheck"
|
|
103
106
|
},
|
|
104
107
|
"peerDependencies": {
|
|
105
108
|
"@stryker-mutator/core": ">=9",
|
|
@@ -124,6 +127,7 @@
|
|
|
124
127
|
"devDependencies": {
|
|
125
128
|
"@eslint/js": "^9.39.5",
|
|
126
129
|
"@stryker-mutator/core": "^10.0.0",
|
|
130
|
+
"@stryker-mutator/jest-runner": "^10.0.0",
|
|
127
131
|
"@stryker-mutator/vitest-runner": "^10.0.0",
|
|
128
132
|
"@types/node": "^22.20.5",
|
|
129
133
|
"@vitest/coverage-istanbul": "^3.2.7",
|
|
@@ -131,6 +135,7 @@
|
|
|
131
135
|
"eslint": "^9.39.5",
|
|
132
136
|
"eslint-config-prettier": "^10.1.8",
|
|
133
137
|
"eslint-plugin-prettier": "^5.5.6",
|
|
138
|
+
"jest": "^30.5.2",
|
|
134
139
|
"prettier": "^3.9.9",
|
|
135
140
|
"tsup": "^8.5.1",
|
|
136
141
|
"typescript": "^5.9.3",
|
|
File without changes
|