@avi2dg/checks 0.43.0 → 0.44.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 +12 -0
- package/README.md +5 -2
- package/{commitlint.config.js → commitlint.config.ts} +1 -1
- package/dist/mutation/mutation-guard-plugin.js +40 -0
- package/dist/mutation/mutation-refusal.js +23 -0
- package/docs/configs/commit-messages.md +2 -2
- package/docs/design.md +20 -0
- package/docs/gates/checks-ci-wiring.md +3 -2
- package/docs/gates/checks-copy.md +113 -0
- package/docs/gates/checks-docs-only.md +3 -2
- package/docs/gates/checks-docs.md +3 -0
- package/docs/gates/checks-flake.md +49 -1
- package/docs/gates/checks-lint.md +2 -1
- package/docs/gates/checks-mutation-baseline.md +4 -2
- package/docs/gates/checks-mutation-compare.md +33 -8
- package/docs/gates/checks-mutation-scope.md +140 -0
- package/docs/gates/checks-mutation.md +11 -7
- package/docs/gates/checks-no-plain-js.md +60 -0
- package/docs/gates/checks-test.md +5 -2
- package/package.json +18 -6
- package/src/core/declared-inputs.ts +76 -0
- package/src/core/gates.ts +4 -0
- package/src/core/git.ts +9 -0
- package/src/delivery/ci-wiring.ts +25 -11
- package/src/delivery/commit-title.ts +2 -2
- package/src/docs/copy-html.ts +110 -0
- package/src/docs/copy-text.ts +70 -0
- package/src/docs/copy.ts +70 -0
- package/src/docs/prose-matchers.ts +14 -14
- package/src/quality/frontend-syntax.ts +9 -52
- package/src/quality/no-plain-js.ts +71 -0
- package/src/testing/flake-preload.ts +4 -0
- package/src/testing/flake.ts +142 -16
- package/src/testing/{mutation-scope.js → mutation-refusal.ts} +11 -9
- package/src/testing/mutation-scope.ts +354 -0
- package/src/testing/mutation.ts +1 -1
- package/src/testing/test-layout.ts +4 -20
- package/src/testing/test-skips.ts +1 -1
- package/src/testing/test.ts +19 -3
- package/stryker.preset.js +3 -3
- package/src/testing/mutation-guard-plugin.js +0 -13
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
#!/usr/bin/env bun
|
|
2
2
|
import { Console, Effect, FileSystem, Path, Schema } from "effect";
|
|
3
|
-
import {
|
|
3
|
+
import { workingTreeFiles } from "../core/git.ts";
|
|
4
4
|
import { runMain } from "../core/main.ts";
|
|
5
5
|
import { ENTRY_POINT, TEST_ENTRY_POINT } from "../core/gates.ts";
|
|
6
6
|
import { identifierName, isRecord, lineOf, parseTypeScript, spanStart, stringValue } from "../core/swc.ts";
|
|
7
|
-
import {
|
|
7
|
+
import { scriptOptions } from "./test.ts";
|
|
8
8
|
|
|
9
9
|
export type Violation = {
|
|
10
10
|
readonly file: string;
|
|
@@ -61,7 +61,6 @@ const ACCEPTED_IGNORES: readonly (readonly string[])[] = [
|
|
|
61
61
|
export const LAYOUT_CHECK_MARK = "src/testing/test-layout.ts";
|
|
62
62
|
export const LAYOUT_CHECK_BIN = "checks-test-layout";
|
|
63
63
|
const OWN_ENTRY_POINT = `src/${ENTRY_POINT.script}`;
|
|
64
|
-
const TEST_SCRIPTS: readonly string[] = [TEST_ENTRY_POINT.bin, `bun src/${TEST_ENTRY_POINT.script}`];
|
|
65
64
|
const PARALLEL_OPTION = "optionally with --parallel=<workers>";
|
|
66
65
|
|
|
67
66
|
export class LayoutError extends Schema.TaggedError<LayoutError>()("LayoutError", {
|
|
@@ -231,12 +230,6 @@ export const isolationViolations = Effect.fn("isolationViolations")(function* (f
|
|
|
231
230
|
return violations;
|
|
232
231
|
});
|
|
233
232
|
|
|
234
|
-
function entryOptions(script: unknown): TestOptions | undefined {
|
|
235
|
-
if (typeof script !== "string") return undefined;
|
|
236
|
-
const entry = TEST_SCRIPTS.find((candidate) => script === candidate || script.startsWith(`${candidate} `));
|
|
237
|
-
return entry === undefined ? undefined : testOptions(script.slice(entry.length).split(" ").filter((arg) => arg !== ""));
|
|
238
|
-
}
|
|
239
|
-
|
|
240
233
|
function tierScriptViolation(scripts: unknown, files: readonly string[], tier: (typeof TEST_TIERS)[number]): Violation | undefined {
|
|
241
234
|
const file = "package.json";
|
|
242
235
|
const key = `test:${tier}`;
|
|
@@ -244,7 +237,7 @@ function tierScriptViolation(scripts: unknown, files: readonly string[], tier: (
|
|
|
244
237
|
const expected = `${TEST_ENTRY_POINT.bin} --tier=${tier}`;
|
|
245
238
|
const hasTests = files.some((candidate) => candidate.startsWith(`tests/${tier}/`) && TEST_FILE.test(candidate));
|
|
246
239
|
if (!hasTests && found === undefined) return undefined;
|
|
247
|
-
if (
|
|
240
|
+
if (scriptOptions(found)?.tier === tier) return undefined;
|
|
248
241
|
return {
|
|
249
242
|
file,
|
|
250
243
|
line: undefined,
|
|
@@ -258,7 +251,7 @@ export function scriptViolations(manifest: unknown, files: readonly string[] = [
|
|
|
258
251
|
const test = isRecord(scripts) ? scripts["test"] : undefined;
|
|
259
252
|
const lint = isRecord(scripts) ? scripts["lint"] : undefined;
|
|
260
253
|
const violations: Violation[] = [];
|
|
261
|
-
const options =
|
|
254
|
+
const options = scriptOptions(test);
|
|
262
255
|
if (options === undefined || options.tier !== undefined) {
|
|
263
256
|
violations.push({
|
|
264
257
|
file,
|
|
@@ -308,15 +301,6 @@ export function bunfigViolations(consumer: unknown, preset: unknown): readonly V
|
|
|
308
301
|
return violations;
|
|
309
302
|
}
|
|
310
303
|
|
|
311
|
-
const workingTreeFiles = Effect.fn("workingTreeFiles")(function* (root: string) {
|
|
312
|
-
const fs = yield* FileSystem.FileSystem;
|
|
313
|
-
const path = yield* Path.Path;
|
|
314
|
-
const listed = yield* git(["ls-files", "-z", "--cached", "--others", "--exclude-standard"], root);
|
|
315
|
-
return yield* Effect.filter(listed.split("\0").filter(Boolean), (file) => fs.exists(path.join(root, file)), {
|
|
316
|
-
concurrency: "unbounded",
|
|
317
|
-
});
|
|
318
|
-
});
|
|
319
|
-
|
|
320
304
|
const parsedToml = Effect.fn("parsedToml")(function* (file: string) {
|
|
321
305
|
const text = yield* (yield* FileSystem.FileSystem).readFileString(file);
|
|
322
306
|
return yield* Effect.try({
|
|
@@ -2,7 +2,7 @@ import { Effect, FileSystem, Path, Schema } from "effect";
|
|
|
2
2
|
import { identifierName, isRecord, lineOf, parseTypeScript, spanStart, stringValue } from "../core/swc.ts";
|
|
3
3
|
|
|
4
4
|
export type Environment = "ci" | "local";
|
|
5
|
-
export type TestTier = "live" | "pixel";
|
|
5
|
+
export type TestTier = "live" | "pixel" | "unit" | "e2e";
|
|
6
6
|
export type SkipDeclaration = {
|
|
7
7
|
readonly file: string;
|
|
8
8
|
readonly line: number;
|
package/src/testing/test.ts
CHANGED
|
@@ -26,10 +26,12 @@ export type TestOptions = {
|
|
|
26
26
|
};
|
|
27
27
|
|
|
28
28
|
const NAME = TEST_ENTRY_POINT.bin;
|
|
29
|
-
const USAGE = `usage: ${NAME} [--tier=live | --tier=pixel] [--parallel=<workers>], and it refuses test filters`;
|
|
29
|
+
const USAGE = `usage: ${NAME} [--tier=live | --tier=pixel | --tier=unit | --tier=e2e] [--parallel=<workers>], and it refuses test filters`;
|
|
30
30
|
const TIERS = new Map<string, TestTier>([
|
|
31
31
|
["--tier=live", "live"],
|
|
32
32
|
["--tier=pixel", "pixel"],
|
|
33
|
+
["--tier=unit", "unit"],
|
|
34
|
+
["--tier=e2e", "e2e"],
|
|
33
35
|
]);
|
|
34
36
|
const PARALLEL = /^--parallel=([1-9][0-9]*)$/;
|
|
35
37
|
|
|
@@ -50,6 +52,14 @@ export function testOptions(args: readonly string[]): TestOptions | undefined {
|
|
|
50
52
|
return options;
|
|
51
53
|
}
|
|
52
54
|
|
|
55
|
+
const TEST_SCRIPTS: readonly string[] = [NAME, `bun src/${TEST_ENTRY_POINT.script}`];
|
|
56
|
+
|
|
57
|
+
export function scriptOptions(script: unknown): TestOptions | undefined {
|
|
58
|
+
if (typeof script !== "string") return undefined;
|
|
59
|
+
const entry = TEST_SCRIPTS.find((candidate) => script === candidate || script.startsWith(`${candidate} `));
|
|
60
|
+
return entry === undefined ? undefined : testOptions(script.slice(entry.length).split(" ").filter((arg) => arg !== ""));
|
|
61
|
+
}
|
|
62
|
+
|
|
53
63
|
function matches(declaration: SkipDeclaration, result: TestResult): boolean {
|
|
54
64
|
const { name } = declaration;
|
|
55
65
|
return (
|
|
@@ -136,11 +146,17 @@ const environmentOf = Config.Boolean("CI").pipe(
|
|
|
136
146
|
Effect.mapError((cause) => new SkipGateError({ message: `cannot tell a ci run from a local one: ${cause.message}` })),
|
|
137
147
|
);
|
|
138
148
|
|
|
149
|
+
function tierArgs(tier: TestTier | undefined): readonly string[] {
|
|
150
|
+
if (tier === undefined) return [];
|
|
151
|
+
// The preset ignores tests/live and tests/pixel, so only those tiers clear the ignore list.
|
|
152
|
+
if (tier === "live" || tier === "pixel") return ["--path-ignore-patterns", "", `./tests/${tier}`];
|
|
153
|
+
return [`./tests/${tier}`];
|
|
154
|
+
}
|
|
155
|
+
|
|
139
156
|
const runSuite = Effect.fn("runSuite")(function* (outfile: string, { tier, workers }: TestOptions) {
|
|
140
157
|
const spawner = yield* ChildProcessSpawner.ChildProcessSpawner;
|
|
141
|
-
const tierArgs = tier === undefined ? [] : ["--path-ignore-patterns", "", `./tests/${tier}`];
|
|
142
158
|
const parallelArgs = workers === undefined ? [] : [`--parallel=${workers}`];
|
|
143
|
-
const args = ["test", "--randomize", ...parallelArgs, ...tierArgs, ...reporterArgs(outfile)];
|
|
159
|
+
const args = ["test", "--randomize", ...parallelArgs, ...tierArgs(tier), ...reporterArgs(outfile)];
|
|
144
160
|
return yield* spawner.exitCode(
|
|
145
161
|
ChildProcess.make(process.execPath, args, {
|
|
146
162
|
stdin: "ignore",
|
package/stryker.preset.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { readdirSync } from "node:fs";
|
|
2
2
|
import { availableParallelism } from "node:os";
|
|
3
3
|
import { fileURLToPath } from "node:url";
|
|
4
|
-
import { GUARD_IGNORER } from "./
|
|
5
|
-
import { fullRunRefusal } from "./
|
|
4
|
+
import { GUARD_IGNORER } from "./dist/mutation/mutation-guard-plugin.js";
|
|
5
|
+
import { fullRunRefusal } from "./dist/mutation/mutation-refusal.js";
|
|
6
6
|
|
|
7
7
|
const [, , command, ...args] = process.argv;
|
|
8
8
|
const refusal = command === "run" ? fullRunRefusal(args, process.env.CI ?? "") : undefined;
|
|
@@ -22,7 +22,7 @@ export default {
|
|
|
22
22
|
plugins: [
|
|
23
23
|
"@stryker-mutator/*",
|
|
24
24
|
"@hughescr/stryker-bun-runner",
|
|
25
|
-
fileURLToPath(new URL("./
|
|
25
|
+
fileURLToPath(new URL("./dist/mutation/mutation-guard-plugin.js", import.meta.url)),
|
|
26
26
|
],
|
|
27
27
|
ignorers: [GUARD_IGNORER],
|
|
28
28
|
testRunner: "bun",
|
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
import { missingReportRefusal } from "./mutation-scope.js";
|
|
2
|
-
|
|
3
|
-
export const GUARD_IGNORER = "checks-incremental-report-guard";
|
|
4
|
-
|
|
5
|
-
// An ignorer is built before instrumentation from the resolved options, so a throw here stops the run before any mutant.
|
|
6
|
-
function incrementalReportGuard(options) {
|
|
7
|
-
const refusal = missingReportRefusal(process.argv.slice(3), process.env.CI ?? "", options.incrementalFile);
|
|
8
|
-
if (refusal !== undefined) throw new Error(refusal);
|
|
9
|
-
return { shouldIgnore: () => undefined };
|
|
10
|
-
}
|
|
11
|
-
incrementalReportGuard.inject = ["options"];
|
|
12
|
-
|
|
13
|
-
export const strykerPlugins = [{ kind: "Ignore", name: GUARD_IGNORER, factory: incrementalReportGuard }];
|