@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.
Files changed (41) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/README.md +5 -2
  3. package/{commitlint.config.js → commitlint.config.ts} +1 -1
  4. package/dist/mutation/mutation-guard-plugin.js +40 -0
  5. package/dist/mutation/mutation-refusal.js +23 -0
  6. package/docs/configs/commit-messages.md +2 -2
  7. package/docs/design.md +20 -0
  8. package/docs/gates/checks-ci-wiring.md +3 -2
  9. package/docs/gates/checks-copy.md +113 -0
  10. package/docs/gates/checks-docs-only.md +3 -2
  11. package/docs/gates/checks-docs.md +3 -0
  12. package/docs/gates/checks-flake.md +49 -1
  13. package/docs/gates/checks-lint.md +2 -1
  14. package/docs/gates/checks-mutation-baseline.md +4 -2
  15. package/docs/gates/checks-mutation-compare.md +33 -8
  16. package/docs/gates/checks-mutation-scope.md +140 -0
  17. package/docs/gates/checks-mutation.md +11 -7
  18. package/docs/gates/checks-no-plain-js.md +60 -0
  19. package/docs/gates/checks-test.md +5 -2
  20. package/package.json +18 -6
  21. package/src/core/declared-inputs.ts +76 -0
  22. package/src/core/gates.ts +4 -0
  23. package/src/core/git.ts +9 -0
  24. package/src/delivery/ci-wiring.ts +25 -11
  25. package/src/delivery/commit-title.ts +2 -2
  26. package/src/docs/copy-html.ts +110 -0
  27. package/src/docs/copy-text.ts +70 -0
  28. package/src/docs/copy.ts +70 -0
  29. package/src/docs/prose-matchers.ts +14 -14
  30. package/src/quality/frontend-syntax.ts +9 -52
  31. package/src/quality/no-plain-js.ts +71 -0
  32. package/src/testing/flake-preload.ts +4 -0
  33. package/src/testing/flake.ts +142 -16
  34. package/src/testing/{mutation-scope.js → mutation-refusal.ts} +11 -9
  35. package/src/testing/mutation-scope.ts +354 -0
  36. package/src/testing/mutation.ts +1 -1
  37. package/src/testing/test-layout.ts +4 -20
  38. package/src/testing/test-skips.ts +1 -1
  39. package/src/testing/test.ts +19 -3
  40. package/stryker.preset.js +3 -3
  41. 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 { git } from "../core/git.ts";
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 { testOptions, type TestOptions } from "./test.ts";
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 (entryOptions(found)?.tier === tier) return undefined;
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 = entryOptions(test);
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;
@@ -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 "./src/testing/mutation-guard-plugin.js";
5
- import { fullRunRefusal } from "./src/testing/mutation-scope.js";
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("./src/testing/mutation-guard-plugin.js", import.meta.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 }];