@t09tanaka/test-gates 0.3.3 → 0.3.4
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 +18 -0
- package/README.md +208 -14
- package/dist/chunk-TOKHAUFX.js +163 -0
- package/dist/{chunk-F7WBEFIL.js → chunk-XMZ6NBV7.js} +34 -10
- package/dist/cli.js +1511 -63
- package/dist/index.cjs +37 -12
- 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 +14 -8
- package/dist/stryker.js +2 -2
- 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/index.cjs
CHANGED
|
@@ -514,6 +514,24 @@ function resolveSettings(raw) {
|
|
|
514
514
|
|
|
515
515
|
// src/core/selfcheck.ts
|
|
516
516
|
var EXCLUDE_ENV = "TEST_GATES_EXCLUDE_SPEC";
|
|
517
|
+
var EXCLUDE_ALL_ENV = "TEST_GATES_EXCLUDE_SPECS";
|
|
518
|
+
function excludedSpecs(env) {
|
|
519
|
+
const one = env[EXCLUDE_ENV];
|
|
520
|
+
const several = env[EXCLUDE_ALL_ENV];
|
|
521
|
+
const excluded = one === void 0 || one === "" ? [] : [one];
|
|
522
|
+
if (several === void 0 || several === "") {
|
|
523
|
+
return excluded;
|
|
524
|
+
}
|
|
525
|
+
let listed = null;
|
|
526
|
+
try {
|
|
527
|
+
listed = JSON.parse(several);
|
|
528
|
+
} catch {
|
|
529
|
+
}
|
|
530
|
+
if (!Array.isArray(listed) || !listed.every((spec) => typeof spec === "string")) {
|
|
531
|
+
throw new Error(`test-gates: ${EXCLUDE_ALL_ENV} must be a JSON list of spec paths`);
|
|
532
|
+
}
|
|
533
|
+
return [...excluded, ...listed];
|
|
534
|
+
}
|
|
517
535
|
var THRESHOLD_MESSAGE = /threshold|coverage data for/i;
|
|
518
536
|
function escapeRegExp(text) {
|
|
519
537
|
return text.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
@@ -561,19 +579,22 @@ function looksLikeSpec(gatePath, settings) {
|
|
|
561
579
|
// src/fs.ts
|
|
562
580
|
var import_node_fs = __toESM(require("fs"), 1);
|
|
563
581
|
var import_node_path = __toESM(require("path"), 1);
|
|
564
|
-
function existsExact(rootDir, relativePath, kind = "file") {
|
|
582
|
+
function existsExact(rootDir, relativePath, kind = "file", listing = /* @__PURE__ */ new Map()) {
|
|
565
583
|
let current = rootDir;
|
|
566
584
|
for (const segment of relativePath.split("/")) {
|
|
567
585
|
if (segment === "" || segment === ".") {
|
|
568
586
|
continue;
|
|
569
587
|
}
|
|
570
|
-
let entries;
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
588
|
+
let entries = listing.get(current);
|
|
589
|
+
if (entries === void 0) {
|
|
590
|
+
try {
|
|
591
|
+
entries = import_node_fs.default.readdirSync(current);
|
|
592
|
+
} catch {
|
|
593
|
+
entries = null;
|
|
594
|
+
}
|
|
595
|
+
listing.set(current, entries);
|
|
575
596
|
}
|
|
576
|
-
if (!entries.includes(segment)) {
|
|
597
|
+
if (entries === null || !entries.includes(segment)) {
|
|
577
598
|
return false;
|
|
578
599
|
}
|
|
579
600
|
current = import_node_path.default.join(current, segment);
|
|
@@ -611,9 +632,12 @@ function readManifest(dir) {
|
|
|
611
632
|
}
|
|
612
633
|
throw error;
|
|
613
634
|
}
|
|
635
|
+
const listing = /* @__PURE__ */ new Map();
|
|
614
636
|
const gates = parsed.gates.map((gate) => {
|
|
615
637
|
const specCandidates = specCandidatesFor(gate.path, settings);
|
|
616
|
-
const existingSpecs = specCandidates.filter(
|
|
638
|
+
const existingSpecs = specCandidates.filter(
|
|
639
|
+
(candidate) => existsExact(absoluteDir, candidate, "file", listing)
|
|
640
|
+
);
|
|
617
641
|
return {
|
|
618
642
|
...gate,
|
|
619
643
|
specCandidates,
|
|
@@ -640,8 +664,8 @@ function loadGates(dir = process.cwd()) {
|
|
|
640
664
|
return manifest;
|
|
641
665
|
}
|
|
642
666
|
function gateSpecs(manifest, env = process.env) {
|
|
643
|
-
const excluded = env
|
|
644
|
-
return manifest.gates.map((gate) => gate.spec).filter((spec) => spec !== null && spec
|
|
667
|
+
const excluded = excludedSpecs(env);
|
|
668
|
+
return manifest.gates.map((gate) => gate.spec).filter((spec) => spec !== null && !excluded.includes(spec));
|
|
645
669
|
}
|
|
646
670
|
|
|
647
671
|
// src/commands/check.ts
|
|
@@ -957,6 +981,7 @@ function isPlainRelativePath(file) {
|
|
|
957
981
|
function checkGates(manifest) {
|
|
958
982
|
const { dir, settings } = manifest;
|
|
959
983
|
const violations = [...manifest.violations];
|
|
984
|
+
const listing = /* @__PURE__ */ new Map();
|
|
960
985
|
for (const gate of manifest.gates) {
|
|
961
986
|
if (!isPlainRelativePath(gate.path)) {
|
|
962
987
|
violations.push({
|
|
@@ -984,7 +1009,7 @@ function checkGates(manifest) {
|
|
|
984
1009
|
});
|
|
985
1010
|
}
|
|
986
1011
|
}
|
|
987
|
-
if (!existsExact(dir, gate.path)) {
|
|
1012
|
+
if (!existsExact(dir, gate.path, "file", listing)) {
|
|
988
1013
|
violations.push({
|
|
989
1014
|
file: gate.path,
|
|
990
1015
|
message: "file not found (the check is case-sensitive)"
|
|
@@ -1013,7 +1038,7 @@ function checkGates(manifest) {
|
|
|
1013
1038
|
violations.push(...scanSpecSource(gate.path, specPath, readText(dir, specPath), settings));
|
|
1014
1039
|
}
|
|
1015
1040
|
for (const candidate of manifest.candidates) {
|
|
1016
|
-
if (!isPlainRelativePath(candidate.path) || !existsExact(dir, candidate.path, "any")) {
|
|
1041
|
+
if (!isPlainRelativePath(candidate.path) || !existsExact(dir, candidate.path, "any", listing)) {
|
|
1017
1042
|
violations.push({
|
|
1018
1043
|
file: MANIFEST_FILE,
|
|
1019
1044
|
message: `${candidate.path}: candidate not found. Remove it, or fix the path`
|
package/dist/index.js
CHANGED
|
@@ -20,7 +20,7 @@ import {
|
|
|
20
20
|
readText,
|
|
21
21
|
resolveSettings,
|
|
22
22
|
validateEquivalentMutants
|
|
23
|
-
} from "./chunk-
|
|
23
|
+
} from "./chunk-XMZ6NBV7.js";
|
|
24
24
|
|
|
25
25
|
// src/commands/check.ts
|
|
26
26
|
import fs from "fs";
|
|
@@ -335,6 +335,7 @@ function isPlainRelativePath(file) {
|
|
|
335
335
|
function checkGates(manifest) {
|
|
336
336
|
const { dir, settings } = manifest;
|
|
337
337
|
const violations = [...manifest.violations];
|
|
338
|
+
const listing = /* @__PURE__ */ new Map();
|
|
338
339
|
for (const gate of manifest.gates) {
|
|
339
340
|
if (!isPlainRelativePath(gate.path)) {
|
|
340
341
|
violations.push({
|
|
@@ -362,7 +363,7 @@ function checkGates(manifest) {
|
|
|
362
363
|
});
|
|
363
364
|
}
|
|
364
365
|
}
|
|
365
|
-
if (!existsExact(dir, gate.path)) {
|
|
366
|
+
if (!existsExact(dir, gate.path, "file", listing)) {
|
|
366
367
|
violations.push({
|
|
367
368
|
file: gate.path,
|
|
368
369
|
message: "file not found (the check is case-sensitive)"
|
|
@@ -391,7 +392,7 @@ function checkGates(manifest) {
|
|
|
391
392
|
violations.push(...scanSpecSource(gate.path, specPath, readText(dir, specPath), settings));
|
|
392
393
|
}
|
|
393
394
|
for (const candidate of manifest.candidates) {
|
|
394
|
-
if (!isPlainRelativePath(candidate.path) || !existsExact(dir, candidate.path, "any")) {
|
|
395
|
+
if (!isPlainRelativePath(candidate.path) || !existsExact(dir, candidate.path, "any", listing)) {
|
|
395
396
|
violations.push({
|
|
396
397
|
file: MANIFEST_FILE,
|
|
397
398
|
message: `${candidate.path}: candidate not found. Remove it, or fix the path`
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __export = (target, all) => {
|
|
9
|
+
for (var name in all)
|
|
10
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
11
|
+
};
|
|
12
|
+
var __copyProps = (to, from, except, desc) => {
|
|
13
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
14
|
+
for (let key of __getOwnPropNames(from))
|
|
15
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
16
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
17
|
+
}
|
|
18
|
+
return to;
|
|
19
|
+
};
|
|
20
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
21
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
22
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
23
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
24
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
25
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
26
|
+
mod
|
|
27
|
+
));
|
|
28
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
29
|
+
|
|
30
|
+
// src/jest-touch-reporter.ts
|
|
31
|
+
var jest_touch_reporter_exports = {};
|
|
32
|
+
__export(jest_touch_reporter_exports, {
|
|
33
|
+
default: () => TouchReporter
|
|
34
|
+
});
|
|
35
|
+
module.exports = __toCommonJS(jest_touch_reporter_exports);
|
|
36
|
+
var import_node_module = require("module");
|
|
37
|
+
var import_node_path2 = __toESM(require("path"), 1);
|
|
38
|
+
|
|
39
|
+
// src/touch.ts
|
|
40
|
+
var import_node_crypto = require("crypto");
|
|
41
|
+
var import_node_fs = __toESM(require("fs"), 1);
|
|
42
|
+
var import_node_path = __toESM(require("path"), 1);
|
|
43
|
+
var import_node_url = require("url");
|
|
44
|
+
var TOUCH_ENV = "TEST_GATES_TOUCH_DIR";
|
|
45
|
+
function isObject(value) {
|
|
46
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
47
|
+
}
|
|
48
|
+
function countersOf(coverage) {
|
|
49
|
+
return {
|
|
50
|
+
s: Object.values(coverage.s ?? {}),
|
|
51
|
+
f: Object.values(coverage.f ?? {}),
|
|
52
|
+
b: Object.values(coverage.b ?? {}).flat()
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
function countersOfAll(coverage) {
|
|
56
|
+
if (!isObject(coverage)) {
|
|
57
|
+
return null;
|
|
58
|
+
}
|
|
59
|
+
const files = {};
|
|
60
|
+
for (const [file, counters] of Object.entries(coverage)) {
|
|
61
|
+
if (!isObject(counters)) {
|
|
62
|
+
return null;
|
|
63
|
+
}
|
|
64
|
+
files[file] = countersOf(counters);
|
|
65
|
+
}
|
|
66
|
+
return files;
|
|
67
|
+
}
|
|
68
|
+
function add(before, now) {
|
|
69
|
+
return before.length === now.length ? before.map((count, index) => count + (now[index] ?? 0)) : [-1];
|
|
70
|
+
}
|
|
71
|
+
function writeSpecNote(directory, spec, coverage) {
|
|
72
|
+
const files = countersOfAll(coverage) ?? {};
|
|
73
|
+
const specs = import_node_path.default.join(directory, "specs");
|
|
74
|
+
import_node_fs.default.mkdirSync(specs, { recursive: true });
|
|
75
|
+
const file = import_node_path.default.join(specs, `${(0, import_node_crypto.createHash)("sha256").update(spec).digest("hex")}.json`);
|
|
76
|
+
let before = {};
|
|
77
|
+
try {
|
|
78
|
+
before = JSON.parse(import_node_fs.default.readFileSync(file, "utf8")).files;
|
|
79
|
+
} catch {
|
|
80
|
+
}
|
|
81
|
+
for (const [name, counters] of Object.entries(before)) {
|
|
82
|
+
const now = files[name];
|
|
83
|
+
files[name] = now === void 0 ? counters : { s: add(counters.s, now.s), f: add(counters.f, now.f), b: add(counters.b, now.b) };
|
|
84
|
+
}
|
|
85
|
+
const note = { spec, files };
|
|
86
|
+
import_node_fs.default.writeFileSync(file, JSON.stringify(note));
|
|
87
|
+
}
|
|
88
|
+
function writeRunInfo(directory, run) {
|
|
89
|
+
import_node_fs.default.mkdirSync(directory, { recursive: true });
|
|
90
|
+
import_node_fs.default.writeFileSync(import_node_path.default.join(directory, "run.json"), JSON.stringify(run));
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// src/jest-touch-reporter.ts
|
|
94
|
+
function jestVersion(rootDir) {
|
|
95
|
+
for (const name of ["jest/package.json", "@jest/core/package.json"]) {
|
|
96
|
+
try {
|
|
97
|
+
const from = (0, import_node_module.createRequire)(import_node_path2.default.join(typeof rootDir === "string" ? rootDir : ".", "x.js"));
|
|
98
|
+
const { version } = from(name);
|
|
99
|
+
if (typeof version === "string") {
|
|
100
|
+
return version;
|
|
101
|
+
}
|
|
102
|
+
} catch {
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
return "unknown";
|
|
106
|
+
}
|
|
107
|
+
var TouchReporter = class {
|
|
108
|
+
constructor(globalConfig = {}) {
|
|
109
|
+
const directory = process.env[TOUCH_ENV];
|
|
110
|
+
if (directory) {
|
|
111
|
+
writeRunInfo(directory, {
|
|
112
|
+
runner: "jest",
|
|
113
|
+
version: jestVersion(globalConfig.rootDir),
|
|
114
|
+
provider: typeof globalConfig.coverageProvider === "string" ? globalConfig.coverageProvider : "unknown",
|
|
115
|
+
pool: "default",
|
|
116
|
+
isolate: true,
|
|
117
|
+
// Without the `projects` option the list is empty, or names the one root.
|
|
118
|
+
projects: Array.isArray(globalConfig.projects) ? Math.max(1, globalConfig.projects.length) : 0,
|
|
119
|
+
problem: null
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
onTestResult(_test, result) {
|
|
124
|
+
const directory = process.env[TOUCH_ENV];
|
|
125
|
+
if (directory) {
|
|
126
|
+
writeSpecNote(directory, result.testFilePath, result.coverage);
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
};
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
interface GlobalConfig {
|
|
2
|
+
rootDir?: unknown;
|
|
3
|
+
coverageProvider?: unknown;
|
|
4
|
+
projects?: unknown;
|
|
5
|
+
}
|
|
6
|
+
/**
|
|
7
|
+
* Jest reporter that `createJestGatesConfig` adds while `test-gates selfcheck` asks for it:
|
|
8
|
+
* writes down what each spec file executed, from the coverage Jest itself collected for that
|
|
9
|
+
* file, and how the run ran.
|
|
10
|
+
*/
|
|
11
|
+
declare class TouchReporter {
|
|
12
|
+
constructor(globalConfig?: GlobalConfig);
|
|
13
|
+
onTestResult(_test: unknown, result: {
|
|
14
|
+
testFilePath: string;
|
|
15
|
+
coverage?: unknown;
|
|
16
|
+
}): void;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export { TouchReporter as default };
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
interface GlobalConfig {
|
|
2
|
+
rootDir?: unknown;
|
|
3
|
+
coverageProvider?: unknown;
|
|
4
|
+
projects?: unknown;
|
|
5
|
+
}
|
|
6
|
+
/**
|
|
7
|
+
* Jest reporter that `createJestGatesConfig` adds while `test-gates selfcheck` asks for it:
|
|
8
|
+
* writes down what each spec file executed, from the coverage Jest itself collected for that
|
|
9
|
+
* file, and how the run ran.
|
|
10
|
+
*/
|
|
11
|
+
declare class TouchReporter {
|
|
12
|
+
constructor(globalConfig?: GlobalConfig);
|
|
13
|
+
onTestResult(_test: unknown, result: {
|
|
14
|
+
testFilePath: string;
|
|
15
|
+
coverage?: unknown;
|
|
16
|
+
}): void;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export { TouchReporter as default };
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import {
|
|
2
|
+
TOUCH_ENV,
|
|
3
|
+
writeRunInfo,
|
|
4
|
+
writeSpecNote
|
|
5
|
+
} from "./chunk-TOKHAUFX.js";
|
|
6
|
+
|
|
7
|
+
// src/jest-touch-reporter.ts
|
|
8
|
+
import { createRequire } from "module";
|
|
9
|
+
import path from "path";
|
|
10
|
+
function jestVersion(rootDir) {
|
|
11
|
+
for (const name of ["jest/package.json", "@jest/core/package.json"]) {
|
|
12
|
+
try {
|
|
13
|
+
const from = createRequire(path.join(typeof rootDir === "string" ? rootDir : ".", "x.js"));
|
|
14
|
+
const { version } = from(name);
|
|
15
|
+
if (typeof version === "string") {
|
|
16
|
+
return version;
|
|
17
|
+
}
|
|
18
|
+
} catch {
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
return "unknown";
|
|
22
|
+
}
|
|
23
|
+
var TouchReporter = class {
|
|
24
|
+
constructor(globalConfig = {}) {
|
|
25
|
+
const directory = process.env[TOUCH_ENV];
|
|
26
|
+
if (directory) {
|
|
27
|
+
writeRunInfo(directory, {
|
|
28
|
+
runner: "jest",
|
|
29
|
+
version: jestVersion(globalConfig.rootDir),
|
|
30
|
+
provider: typeof globalConfig.coverageProvider === "string" ? globalConfig.coverageProvider : "unknown",
|
|
31
|
+
pool: "default",
|
|
32
|
+
isolate: true,
|
|
33
|
+
// Without the `projects` option the list is empty, or names the one root.
|
|
34
|
+
projects: Array.isArray(globalConfig.projects) ? Math.max(1, globalConfig.projects.length) : 0,
|
|
35
|
+
problem: null
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
onTestResult(_test, result) {
|
|
40
|
+
const directory = process.env[TOUCH_ENV];
|
|
41
|
+
if (directory) {
|
|
42
|
+
writeSpecNote(directory, result.testFilePath, result.coverage);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
};
|
|
46
|
+
export {
|
|
47
|
+
TouchReporter as default
|
|
48
|
+
};
|
package/dist/jest.cjs
CHANGED
|
@@ -33,7 +33,13 @@ __export(jest_exports, {
|
|
|
33
33
|
createJestGatesConfig: () => createJestGatesConfig
|
|
34
34
|
});
|
|
35
35
|
module.exports = __toCommonJS(jest_exports);
|
|
36
|
-
|
|
36
|
+
|
|
37
|
+
// node_modules/tsup/assets/cjs_shims.js
|
|
38
|
+
var getImportMetaUrl = () => typeof document === "undefined" ? new URL(`file:${__filename}`).href : document.currentScript && document.currentScript.tagName.toUpperCase() === "SCRIPT" ? document.currentScript.src : new URL("main.js", document.baseURI).href;
|
|
39
|
+
var importMetaUrl = /* @__PURE__ */ getImportMetaUrl();
|
|
40
|
+
|
|
41
|
+
// src/jest.ts
|
|
42
|
+
var import_node_path5 = __toESM(require("path"), 1);
|
|
37
43
|
|
|
38
44
|
// src/load.ts
|
|
39
45
|
var import_node_fs2 = __toESM(require("fs"), 1);
|
|
@@ -490,6 +496,24 @@ function resolveSettings(raw) {
|
|
|
490
496
|
|
|
491
497
|
// src/core/selfcheck.ts
|
|
492
498
|
var EXCLUDE_ENV = "TEST_GATES_EXCLUDE_SPEC";
|
|
499
|
+
var EXCLUDE_ALL_ENV = "TEST_GATES_EXCLUDE_SPECS";
|
|
500
|
+
function excludedSpecs(env) {
|
|
501
|
+
const one = env[EXCLUDE_ENV];
|
|
502
|
+
const several = env[EXCLUDE_ALL_ENV];
|
|
503
|
+
const excluded = one === void 0 || one === "" ? [] : [one];
|
|
504
|
+
if (several === void 0 || several === "") {
|
|
505
|
+
return excluded;
|
|
506
|
+
}
|
|
507
|
+
let listed = null;
|
|
508
|
+
try {
|
|
509
|
+
listed = JSON.parse(several);
|
|
510
|
+
} catch {
|
|
511
|
+
}
|
|
512
|
+
if (!Array.isArray(listed) || !listed.every((spec) => typeof spec === "string")) {
|
|
513
|
+
throw new Error(`test-gates: ${EXCLUDE_ALL_ENV} must be a JSON list of spec paths`);
|
|
514
|
+
}
|
|
515
|
+
return [...excluded, ...listed];
|
|
516
|
+
}
|
|
493
517
|
|
|
494
518
|
// src/core/spec-path.ts
|
|
495
519
|
function gateExtensionOf(gatePath, gateExtensions) {
|
|
@@ -514,19 +538,22 @@ function specCandidatesFor(gatePath, settings) {
|
|
|
514
538
|
// src/fs.ts
|
|
515
539
|
var import_node_fs = __toESM(require("fs"), 1);
|
|
516
540
|
var import_node_path = __toESM(require("path"), 1);
|
|
517
|
-
function existsExact(rootDir, relativePath, kind = "file") {
|
|
541
|
+
function existsExact(rootDir, relativePath, kind = "file", listing = /* @__PURE__ */ new Map()) {
|
|
518
542
|
let current = rootDir;
|
|
519
543
|
for (const segment of relativePath.split("/")) {
|
|
520
544
|
if (segment === "" || segment === ".") {
|
|
521
545
|
continue;
|
|
522
546
|
}
|
|
523
|
-
let entries;
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
547
|
+
let entries = listing.get(current);
|
|
548
|
+
if (entries === void 0) {
|
|
549
|
+
try {
|
|
550
|
+
entries = import_node_fs.default.readdirSync(current);
|
|
551
|
+
} catch {
|
|
552
|
+
entries = null;
|
|
553
|
+
}
|
|
554
|
+
listing.set(current, entries);
|
|
528
555
|
}
|
|
529
|
-
if (!entries.includes(segment)) {
|
|
556
|
+
if (entries === null || !entries.includes(segment)) {
|
|
530
557
|
return false;
|
|
531
558
|
}
|
|
532
559
|
current = import_node_path.default.join(current, segment);
|
|
@@ -561,9 +588,12 @@ function readManifest(dir) {
|
|
|
561
588
|
}
|
|
562
589
|
throw error;
|
|
563
590
|
}
|
|
591
|
+
const listing = /* @__PURE__ */ new Map();
|
|
564
592
|
const gates = parsed.gates.map((gate) => {
|
|
565
593
|
const specCandidates = specCandidatesFor(gate.path, settings);
|
|
566
|
-
const existingSpecs = specCandidates.filter(
|
|
594
|
+
const existingSpecs = specCandidates.filter(
|
|
595
|
+
(candidate) => existsExact(absoluteDir, candidate, "file", listing)
|
|
596
|
+
);
|
|
567
597
|
return {
|
|
568
598
|
...gate,
|
|
569
599
|
specCandidates,
|
|
@@ -590,8 +620,8 @@ function loadGates(dir = process.cwd()) {
|
|
|
590
620
|
return manifest;
|
|
591
621
|
}
|
|
592
622
|
function gateSpecs(manifest, env = process.env) {
|
|
593
|
-
const excluded = env
|
|
594
|
-
return manifest.gates.map((gate) => gate.spec).filter((spec) => spec !== null && spec
|
|
623
|
+
const excluded = excludedSpecs(env);
|
|
624
|
+
return manifest.gates.map((gate) => gate.spec).filter((spec) => spec !== null && !excluded.includes(spec));
|
|
595
625
|
}
|
|
596
626
|
var NO_SPEC_PATTERN = "__test_gates_no_spec_left__";
|
|
597
627
|
|
|
@@ -609,6 +639,21 @@ function resolveRootDir(rootDir) {
|
|
|
609
639
|
return import_node_path3.default.resolve(rootDir);
|
|
610
640
|
}
|
|
611
641
|
|
|
642
|
+
// src/touch.ts
|
|
643
|
+
var import_node_crypto = require("crypto");
|
|
644
|
+
var import_node_fs3 = __toESM(require("fs"), 1);
|
|
645
|
+
var import_node_path4 = __toESM(require("path"), 1);
|
|
646
|
+
var import_node_url2 = require("url");
|
|
647
|
+
var TOUCH_ENV = "TEST_GATES_TOUCH_DIR";
|
|
648
|
+
function jestReporterFile(moduleUrl) {
|
|
649
|
+
const directory = import_node_path4.default.dirname((0, import_node_url2.fileURLToPath)(moduleUrl));
|
|
650
|
+
const found = [".cjs", ".ts"].map((extension) => import_node_path4.default.join(directory, `jest-touch-reporter${extension}`)).find((file) => import_node_fs3.default.existsSync(file));
|
|
651
|
+
if (found === void 0) {
|
|
652
|
+
throw new Error(`test-gates: jest-touch-reporter is missing in ${directory}`);
|
|
653
|
+
}
|
|
654
|
+
return found;
|
|
655
|
+
}
|
|
656
|
+
|
|
612
657
|
// src/jest.ts
|
|
613
658
|
function createJestGatesConfig(options = {}) {
|
|
614
659
|
const { rootDir: rootDirOption, ...overrides } = options;
|
|
@@ -620,6 +665,19 @@ function createJestGatesConfig(options = {}) {
|
|
|
620
665
|
coverageReporters: ["text"],
|
|
621
666
|
coverageDirectory: "<rootDir>/coverage/gates",
|
|
622
667
|
...overrides,
|
|
668
|
+
// While `test-gates selfcheck` asks for it: write down what each spec executed.
|
|
669
|
+
...process.env[TOUCH_ENV] ? {
|
|
670
|
+
reporters: [
|
|
671
|
+
...Array.isArray(overrides.reporters) ? overrides.reporters : ["default"],
|
|
672
|
+
jestReporterFile(importMetaUrl)
|
|
673
|
+
],
|
|
674
|
+
// The runner's own total, to check the notes against.
|
|
675
|
+
coverageReporters: [
|
|
676
|
+
...Array.isArray(overrides.coverageReporters) ? overrides.coverageReporters : ["text"],
|
|
677
|
+
"json"
|
|
678
|
+
],
|
|
679
|
+
coverageDirectory: import_node_path5.default.join(process.env[TOUCH_ENV], "coverage")
|
|
680
|
+
} : {},
|
|
623
681
|
rootDir,
|
|
624
682
|
// Never an empty array: Jest would fall back to its default pattern and run every test.
|
|
625
683
|
testMatch: (specs.length > 0 ? specs : [NO_SPEC_PATTERN]).map((spec) => `<rootDir>/${spec}`),
|
|
@@ -630,7 +688,7 @@ function createJestGatesConfig(options = {}) {
|
|
|
630
688
|
coverageProvider: "babel",
|
|
631
689
|
collectCoverageFrom: manifest.gates.map((gate) => `<rootDir>/${gate.path}`),
|
|
632
690
|
coverageThreshold: Object.fromEntries(
|
|
633
|
-
manifest.gates.map((gate) => [
|
|
691
|
+
manifest.gates.map((gate) => [import_node_path5.default.join(rootDir, gate.path), { ...FULL_COVERAGE }])
|
|
634
692
|
)
|
|
635
693
|
};
|
|
636
694
|
}
|
package/dist/jest.js
CHANGED
|
@@ -1,12 +1,16 @@
|
|
|
1
|
+
import {
|
|
2
|
+
TOUCH_ENV,
|
|
3
|
+
jestReporterFile
|
|
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-XMZ6NBV7.js";
|
|
10
14
|
|
|
11
15
|
// src/jest.ts
|
|
12
16
|
import path from "path";
|
|
@@ -20,6 +24,19 @@ function createJestGatesConfig(options = {}) {
|
|
|
20
24
|
coverageReporters: ["text"],
|
|
21
25
|
coverageDirectory: "<rootDir>/coverage/gates",
|
|
22
26
|
...overrides,
|
|
27
|
+
// While `test-gates selfcheck` asks for it: write down what each spec executed.
|
|
28
|
+
...process.env[TOUCH_ENV] ? {
|
|
29
|
+
reporters: [
|
|
30
|
+
...Array.isArray(overrides.reporters) ? overrides.reporters : ["default"],
|
|
31
|
+
jestReporterFile(import.meta.url)
|
|
32
|
+
],
|
|
33
|
+
// The runner's own total, to check the notes against.
|
|
34
|
+
coverageReporters: [
|
|
35
|
+
...Array.isArray(overrides.coverageReporters) ? overrides.coverageReporters : ["text"],
|
|
36
|
+
"json"
|
|
37
|
+
],
|
|
38
|
+
coverageDirectory: path.join(process.env[TOUCH_ENV], "coverage")
|
|
39
|
+
} : {},
|
|
23
40
|
rootDir,
|
|
24
41
|
// Never an empty array: Jest would fall back to its default pattern and run every test.
|
|
25
42
|
testMatch: (specs.length > 0 ? specs : [NO_SPEC_PATTERN]).map((spec) => `<rootDir>/${spec}`),
|
package/dist/stryker.cjs
CHANGED
|
@@ -511,19 +511,22 @@ function specCandidatesFor(gatePath, settings) {
|
|
|
511
511
|
// src/fs.ts
|
|
512
512
|
var import_node_fs = __toESM(require("fs"), 1);
|
|
513
513
|
var import_node_path = __toESM(require("path"), 1);
|
|
514
|
-
function existsExact(rootDir, relativePath, kind = "file") {
|
|
514
|
+
function existsExact(rootDir, relativePath, kind = "file", listing = /* @__PURE__ */ new Map()) {
|
|
515
515
|
let current = rootDir;
|
|
516
516
|
for (const segment of relativePath.split("/")) {
|
|
517
517
|
if (segment === "" || segment === ".") {
|
|
518
518
|
continue;
|
|
519
519
|
}
|
|
520
|
-
let entries;
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
520
|
+
let entries = listing.get(current);
|
|
521
|
+
if (entries === void 0) {
|
|
522
|
+
try {
|
|
523
|
+
entries = import_node_fs.default.readdirSync(current);
|
|
524
|
+
} catch {
|
|
525
|
+
entries = null;
|
|
526
|
+
}
|
|
527
|
+
listing.set(current, entries);
|
|
525
528
|
}
|
|
526
|
-
if (!entries.includes(segment)) {
|
|
529
|
+
if (entries === null || !entries.includes(segment)) {
|
|
527
530
|
return false;
|
|
528
531
|
}
|
|
529
532
|
current = import_node_path.default.join(current, segment);
|
|
@@ -558,9 +561,12 @@ function readManifest(dir) {
|
|
|
558
561
|
}
|
|
559
562
|
throw error;
|
|
560
563
|
}
|
|
564
|
+
const listing = /* @__PURE__ */ new Map();
|
|
561
565
|
const gates = parsed.gates.map((gate) => {
|
|
562
566
|
const specCandidates = specCandidatesFor(gate.path, settings);
|
|
563
|
-
const existingSpecs = specCandidates.filter(
|
|
567
|
+
const existingSpecs = specCandidates.filter(
|
|
568
|
+
(candidate) => existsExact(absoluteDir, candidate, "file", listing)
|
|
569
|
+
);
|
|
564
570
|
return {
|
|
565
571
|
...gate,
|
|
566
572
|
specCandidates,
|
package/dist/stryker.js
CHANGED