@elench/testkit 0.1.82 → 0.1.84

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 (100) hide show
  1. package/README.md +37 -7
  2. package/lib/cli/agents/index.mjs +64 -0
  3. package/lib/cli/agents/investigate.mjs +75 -0
  4. package/lib/cli/agents/investigation-context.mjs +102 -0
  5. package/lib/cli/agents/prompt-builder.mjs +25 -0
  6. package/lib/cli/agents/providers/claude.mjs +74 -0
  7. package/lib/cli/agents/providers/codex.mjs +83 -0
  8. package/lib/cli/agents/providers/shared.mjs +134 -0
  9. package/lib/cli/command-helpers.mjs +53 -25
  10. package/lib/cli/commands/investigate.mjs +87 -0
  11. package/lib/cli/entrypoint.mjs +3 -0
  12. package/lib/cli/presentation/colors.mjs +12 -0
  13. package/lib/cli/presentation/events-reporter.mjs +135 -0
  14. package/lib/cli/presentation/summary-box.mjs +11 -11
  15. package/lib/cli/presentation/tree-reporter.mjs +159 -0
  16. package/lib/cli/tui/run-app.mjs +1 -0
  17. package/lib/cli/tui/run-session-app.mjs +370 -0
  18. package/lib/cli/tui/run-session-state.mjs +481 -0
  19. package/lib/cli/tui/run-tree-state.mjs +1 -0
  20. package/lib/config-api/auth-fixtures.mjs +15 -10
  21. package/lib/discovery/index.mjs +1 -1
  22. package/lib/index.d.ts +5 -1
  23. package/lib/runner/orchestrator.mjs +1 -0
  24. package/lib/runtime/index.d.ts +138 -5
  25. package/lib/runtime/index.mjs +68 -2
  26. package/lib/runtime-src/k6/http-assertions.js +31 -1
  27. package/lib/runtime-src/k6/http-checks.js +120 -0
  28. package/lib/runtime-src/k6/http-suite-runtime.js +5 -1
  29. package/lib/runtime-src/k6/http.js +213 -23
  30. package/lib/runtime-src/shared/error-body.mjs +42 -0
  31. package/lib/runtime-src/shared/http-parsing.mjs +68 -0
  32. package/node_modules/@elench/next-analysis/package.json +1 -1
  33. package/node_modules/@elench/testkit-bridge/package.json +2 -2
  34. package/node_modules/@elench/testkit-protocol/package.json +1 -1
  35. package/node_modules/@elench/ts-analysis/package.json +1 -1
  36. package/package.json +7 -6
  37. package/lib/app/configs.test.mjs +0 -34
  38. package/lib/app/typecheck.test.mjs +0 -24
  39. package/lib/bundler/index.test.mjs +0 -164
  40. package/lib/cli/args.test.mjs +0 -110
  41. package/lib/cli/presentation/code-frames.test.mjs +0 -71
  42. package/lib/cli/presentation/run-reporter.test.mjs +0 -192
  43. package/lib/cli/presentation/summary-box.test.mjs +0 -43
  44. package/lib/cli/presentation/terminal-layout.test.mjs +0 -23
  45. package/lib/config/database.test.mjs +0 -29
  46. package/lib/config/discovery.test.mjs +0 -276
  47. package/lib/config/env.test.mjs +0 -40
  48. package/lib/config/index.test.mjs +0 -44
  49. package/lib/config/paths.test.mjs +0 -27
  50. package/lib/config/runtime.test.mjs +0 -82
  51. package/lib/config/skip-config.test.mjs +0 -63
  52. package/lib/config-api/index.test.mjs +0 -344
  53. package/lib/config-api/next-runtime-tsconfig.test.mjs +0 -58
  54. package/lib/coverage/backend-discovery.test.mjs +0 -61
  55. package/lib/coverage/evidence.test.mjs +0 -87
  56. package/lib/coverage/index.test.mjs +0 -715
  57. package/lib/coverage/routing.test.mjs +0 -36
  58. package/lib/coverage/shared.test.mjs +0 -72
  59. package/lib/database/fingerprint.test.mjs +0 -99
  60. package/lib/database/index.test.mjs +0 -95
  61. package/lib/database/naming.test.mjs +0 -39
  62. package/lib/database/state.test.mjs +0 -66
  63. package/lib/database/template-steps.test.mjs +0 -43
  64. package/lib/discovery/file-metadata.test.mjs +0 -51
  65. package/lib/discovery/index.test.mjs +0 -182
  66. package/lib/discovery/path-policy.test.mjs +0 -65
  67. package/lib/drizzle/index.test.mjs +0 -33
  68. package/lib/env/index.test.mjs +0 -82
  69. package/lib/history/index.test.mjs +0 -115
  70. package/lib/package.test.mjs +0 -59
  71. package/lib/playwright/index.test.mjs +0 -43
  72. package/lib/regressions/github.test.mjs +0 -324
  73. package/lib/regressions/index.test.mjs +0 -187
  74. package/lib/reporters/playwright.test.mjs +0 -167
  75. package/lib/runner/default-runtime-errors.test.mjs +0 -49
  76. package/lib/runner/execution-config.test.mjs +0 -67
  77. package/lib/runner/failure-details.test.mjs +0 -114
  78. package/lib/runner/formatting.test.mjs +0 -205
  79. package/lib/runner/metadata.test.mjs +0 -52
  80. package/lib/runner/planning.test.mjs +0 -371
  81. package/lib/runner/playwright-config.test.mjs +0 -78
  82. package/lib/runner/processes.test.mjs +0 -21
  83. package/lib/runner/regressions.test.mjs +0 -168
  84. package/lib/runner/reporting.test.mjs +0 -310
  85. package/lib/runner/results.test.mjs +0 -376
  86. package/lib/runner/runtime-manager.test.mjs +0 -252
  87. package/lib/runner/runtime-preparation.test.mjs +0 -141
  88. package/lib/runner/selection.test.mjs +0 -24
  89. package/lib/runner/setup-operations.test.mjs +0 -94
  90. package/lib/runner/state.test.mjs +0 -62
  91. package/lib/runner/suite-selection.test.mjs +0 -49
  92. package/lib/runner/template.test.mjs +0 -272
  93. package/lib/shared/build-config.test.mjs +0 -132
  94. package/lib/shared/configured-steps.test.mjs +0 -102
  95. package/lib/shared/execution-schema.test.mjs +0 -26
  96. package/lib/shared/file-timeout.test.mjs +0 -64
  97. package/lib/shared/test-context.test.mjs +0 -43
  98. package/lib/timing/index.test.mjs +0 -64
  99. package/lib/toolchains/index.test.mjs +0 -168
  100. package/lib/vitest/index.test.mjs +0 -20
@@ -1,65 +0,0 @@
1
- import fs from "fs";
2
- import os from "os";
3
- import path from "path";
4
- import { afterEach, describe, expect, it } from "vitest";
5
- import {
6
- mergeDiscoveryConfigs,
7
- normalizeDiscoveryConfig,
8
- resolveDiscoveryRoots,
9
- shouldExcludeDiscoveryPath,
10
- } from "./path-policy.mjs";
11
-
12
- const cleanups = [];
13
-
14
- afterEach(() => {
15
- while (cleanups.length > 0) {
16
- cleanups.pop()();
17
- }
18
- });
19
-
20
- describe("discovery path policy", () => {
21
- it("normalizes roots and excludes as relative paths", () => {
22
- expect(
23
- normalizeDiscoveryConfig({
24
- roots: ["./app/", "src/app"],
25
- exclude: ["./coverage/", "dist"],
26
- })
27
- ).toEqual({
28
- roots: ["app", "src/app"],
29
- exclude: ["coverage", "dist"],
30
- });
31
- });
32
-
33
- it("merges excludes without losing later roots", () => {
34
- expect(
35
- mergeDiscoveryConfigs(
36
- { roots: ["src"], exclude: ["coverage"] },
37
- { exclude: ["dist"] },
38
- { roots: ["app"], exclude: ["test-results"] }
39
- )
40
- ).toEqual({
41
- roots: ["app"],
42
- exclude: ["coverage", "dist", "test-results"],
43
- });
44
- });
45
-
46
- it("treats exclude paths as path prefixes rather than bare directory names", () => {
47
- expect(shouldExcludeDiscoveryPath("coverage", ["coverage"])).toBe(true);
48
- expect(shouldExcludeDiscoveryPath("coverage/report/index.html", ["coverage"])).toBe(true);
49
- expect(shouldExcludeDiscoveryPath("app/coverage", ["coverage"])).toBe(false);
50
- expect(shouldExcludeDiscoveryPath("src/app/coverage", ["coverage"])).toBe(false);
51
- });
52
-
53
- it("resolves only existing discovery roots", () => {
54
- const productDir = fs.mkdtempSync(path.join(os.tmpdir(), "testkit-discovery-roots-"));
55
- cleanups.push(() => fs.rmSync(productDir, { recursive: true, force: true }));
56
- fs.mkdirSync(path.join(productDir, "app"), { recursive: true });
57
- fs.mkdirSync(path.join(productDir, "src", "app"), { recursive: true });
58
-
59
- const roots = resolveDiscoveryRoots(productDir, ["app", "missing", "src/app"]);
60
- expect(roots).toEqual([
61
- path.join(productDir, "app"),
62
- path.join(productDir, "src", "app"),
63
- ]);
64
- });
65
- });
@@ -1,33 +0,0 @@
1
- import fs from "fs";
2
- import os from "os";
3
- import path from "path";
4
- import { describe, expect, it } from "vitest";
5
- import { defineConfig } from "./index.mjs";
6
-
7
- describe("testkit drizzle defineConfig", () => {
8
- it("loads dotenv files before returning config", () => {
9
- const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "testkit-drizzle-"));
10
- fs.writeFileSync(path.join(tmpDir, ".env"), "DATABASE_URL=postgres://user:pass@127.0.0.1:5432/app\n");
11
- const env = {};
12
-
13
- const config = defineConfig({ dialect: "postgresql" }, { cwd: tmpDir, env });
14
-
15
- expect(config).toEqual({ dialect: "postgresql" });
16
- expect(env.DATABASE_URL).toContain("127.0.0.1");
17
- fs.rmSync(tmpDir, { recursive: true, force: true });
18
- });
19
-
20
- it("enforces local database urls during managed runs by default", () => {
21
- expect(() =>
22
- defineConfig(
23
- { dialect: "postgresql" },
24
- {
25
- env: {
26
- TESTKIT_ACTIVE: "1",
27
- DATABASE_URL: "postgres://user:pass@db.example.com:5432/app",
28
- },
29
- }
30
- )
31
- ).toThrow("testkit runs require a local PostgreSQL DATABASE_URL");
32
- });
33
- });
@@ -1,82 +0,0 @@
1
- import fs from "fs";
2
- import os from "os";
3
- import path from "path";
4
- import { describe, expect, it } from "vitest";
5
- import {
6
- assertLocalDatabaseUrl,
7
- isManagedRuntime,
8
- loadDotenvFiles,
9
- shouldLoadDotenv,
10
- } from "./index.mjs";
11
-
12
- describe("testkit env helpers", () => {
13
- it("detects managed runtimes", () => {
14
- expect(isManagedRuntime({ TESTKIT_ACTIVE: "1" })).toBe(true);
15
- expect(isManagedRuntime({ TESTKIT_ACTIVE: "0" })).toBe(false);
16
- });
17
-
18
- it("only loads dotenv when not production and not managed", () => {
19
- expect(shouldLoadDotenv({ NODE_ENV: "development" })).toBe(true);
20
- expect(shouldLoadDotenv({ NODE_ENV: "production" })).toBe(false);
21
- expect(shouldLoadDotenv({ NODE_ENV: "development", TESTKIT_ACTIVE: "1" })).toBe(false);
22
- });
23
-
24
- it("loads dotenv files in order and respects override by default", () => {
25
- const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "testkit-env-"));
26
- fs.writeFileSync(path.join(tmpDir, ".env"), "FOO=base\nBAR=base\n");
27
- fs.writeFileSync(path.join(tmpDir, ".env.local"), "BAR=local\nBAZ=local\n");
28
- const env = {};
29
-
30
- const result = loadDotenvFiles({ cwd: tmpDir, env });
31
-
32
- expect(result.loaded).toHaveLength(2);
33
- expect(env).toEqual({
34
- FOO: "base",
35
- BAR: "local",
36
- BAZ: "local",
37
- });
38
- fs.rmSync(tmpDir, { recursive: true, force: true });
39
- });
40
-
41
- it("supports non-overriding dotenv loads", () => {
42
- const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "testkit-env-"));
43
- fs.writeFileSync(path.join(tmpDir, ".env"), "FOO=base\n");
44
- const env = { FOO: "preset" };
45
-
46
- loadDotenvFiles({ cwd: tmpDir, env, override: false });
47
-
48
- expect(env.FOO).toBe("preset");
49
- fs.rmSync(tmpDir, { recursive: true, force: true });
50
- });
51
-
52
- it("skips dotenv loading when managed unless explicitly overridden", () => {
53
- const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "testkit-env-"));
54
- fs.writeFileSync(path.join(tmpDir, ".env"), "FOO=base\n");
55
- const env = { TESTKIT_ACTIVE: "1" };
56
-
57
- const skipped = loadDotenvFiles({ cwd: tmpDir, env });
58
- expect(skipped.loaded).toEqual([]);
59
- expect(env.FOO).toBeUndefined();
60
-
61
- const forced = loadDotenvFiles({ cwd: tmpDir, env, onlyWhenAllowed: false });
62
- expect(forced.loaded).toHaveLength(1);
63
- expect(env.FOO).toBe("base");
64
- fs.rmSync(tmpDir, { recursive: true, force: true });
65
- });
66
-
67
- it("enforces local postgres urls during managed runs", () => {
68
- expect(() =>
69
- assertLocalDatabaseUrl(
70
- { TESTKIT_ACTIVE: "1", DATABASE_URL: "postgres://user:pass@db.example.com:5432/app" },
71
- "drizzle.config.ts"
72
- )
73
- ).toThrow("drizzle.config.ts testkit runs require a local PostgreSQL DATABASE_URL.");
74
-
75
- expect(() =>
76
- assertLocalDatabaseUrl(
77
- { TESTKIT_ACTIVE: "1", DATABASE_URL: "postgres://user:pass@127.0.0.1:5432/app" },
78
- "drizzle.config.ts"
79
- )
80
- ).not.toThrow();
81
- });
82
- });
@@ -1,115 +0,0 @@
1
- import { describe, expect, it } from "vitest";
2
- import { buildHistoryTestId, createEmptyHistory, summarizeHistoryForFiles, updateHistoryFromRunArtifact } from "./index.mjs";
3
-
4
- describe("test history", () => {
5
- it("tracks first seen and pass/fail counters across runs", () => {
6
- const history = createEmptyHistory();
7
- const first = updateHistoryFromRunArtifact(
8
- history,
9
- {
10
- generatedAt: "2026-04-30T10:00:00.000Z",
11
- services: [
12
- {
13
- name: "api",
14
- suites: [
15
- {
16
- name: "routes",
17
- type: "int",
18
- framework: "default",
19
- files: [
20
- {
21
- path: "src/api/routes/__testkit__/health.int.testkit.ts",
22
- status: "passed",
23
- durationMs: 2000,
24
- },
25
- ],
26
- },
27
- ],
28
- },
29
- ],
30
- }
31
- );
32
- const second = updateHistoryFromRunArtifact(
33
- first,
34
- {
35
- generatedAt: "2026-05-01T10:00:00.000Z",
36
- services: [
37
- {
38
- name: "api",
39
- suites: [
40
- {
41
- name: "routes",
42
- type: "int",
43
- framework: "default",
44
- files: [
45
- {
46
- path: "src/api/routes/__testkit__/health.int.testkit.ts",
47
- status: "failed",
48
- durationMs: 4000,
49
- },
50
- ],
51
- },
52
- ],
53
- },
54
- ],
55
- }
56
- );
57
-
58
- const id = buildHistoryTestId("api", "int", "src/api/routes/__testkit__/health.int.testkit.ts");
59
- expect(second.tests[id]).toMatchObject({
60
- firstSeenAt: "2026-04-30T10:00:00.000Z",
61
- lastSeenAt: "2026-05-01T10:00:00.000Z",
62
- lastRunAt: "2026-05-01T10:00:00.000Z",
63
- runCount: 2,
64
- passCount: 1,
65
- failCount: 1,
66
- skipCount: 0,
67
- avgDurationMs: 3000,
68
- lastStatus: "failed",
69
- });
70
- });
71
-
72
- it("summarizes history for discovery file entries", () => {
73
- const history = {
74
- version: 1,
75
- tests: {
76
- [buildHistoryTestId("api", "int", "src/api/routes/__testkit__/health.int.testkit.ts")]: {
77
- id: buildHistoryTestId("api", "int", "src/api/routes/__testkit__/health.int.testkit.ts"),
78
- path: "src/api/routes/__testkit__/health.int.testkit.ts",
79
- service: "api",
80
- suiteName: "routes",
81
- selectionType: "int",
82
- framework: "k6",
83
- firstSeenAt: "2026-04-30T10:00:00.000Z",
84
- lastSeenAt: "2026-05-01T10:00:00.000Z",
85
- lastRunAt: "2026-05-01T10:00:00.000Z",
86
- runCount: 2,
87
- passCount: 1,
88
- failCount: 1,
89
- skipCount: 0,
90
- notRunCount: 0,
91
- avgDurationMs: 3000,
92
- durationCount: 2,
93
- lastStatus: "failed",
94
- },
95
- },
96
- };
97
-
98
- const summaries = summarizeHistoryForFiles(history, [
99
- {
100
- id: buildHistoryTestId("api", "int", "src/api/routes/__testkit__/health.int.testkit.ts"),
101
- service: "api",
102
- selectionType: "int",
103
- path: "src/api/routes/__testkit__/health.int.testkit.ts",
104
- },
105
- ]);
106
-
107
- expect(summaries.get(buildHistoryTestId("api", "int", "src/api/routes/__testkit__/health.int.testkit.ts"))).toMatchObject({
108
- runCount: 2,
109
- passCount: 1,
110
- failCount: 1,
111
- avgDurationMs: 3000,
112
- lastStatus: "failed",
113
- });
114
- });
115
- });
@@ -1,59 +0,0 @@
1
- import fs from "fs";
2
- import path from "path";
3
- import { fileURLToPath } from "url";
4
- import { describe, expect, it } from "vitest";
5
-
6
- const rootDir = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
7
- const packageJsonPath = path.join(rootDir, "package.json");
8
- const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, "utf8"));
9
-
10
- describe("package metadata", () => {
11
- it("ships first-party type declarations for the public exports", () => {
12
- expect(packageJson.types).toBe("./lib/index.d.ts");
13
- expect(packageJson.exports["."]).toEqual({
14
- types: "./lib/index.d.ts",
15
- default: "./lib/index.mjs",
16
- });
17
- expect(packageJson.exports["./config"]).toEqual({
18
- types: "./lib/config-api/index.d.ts",
19
- default: "./lib/config-api/index.mjs",
20
- });
21
- expect(packageJson.exports["./drizzle"]).toEqual({
22
- types: "./lib/drizzle/index.d.ts",
23
- default: "./lib/drizzle/index.mjs",
24
- });
25
- expect(packageJson.exports["./env"]).toEqual({
26
- types: "./lib/env/index.d.ts",
27
- default: "./lib/env/index.mjs",
28
- });
29
- expect(packageJson.exports["./playwright"]).toEqual({
30
- types: "./lib/playwright/index.d.ts",
31
- default: "./lib/playwright/index.mjs",
32
- });
33
- expect(packageJson.exports["./runtime"]).toEqual({
34
- types: "./lib/runtime/index.d.ts",
35
- default: "./lib/runtime/index.mjs",
36
- });
37
- expect(packageJson.exports["./vitest"]).toEqual({
38
- types: "./lib/vitest/index.d.ts",
39
- default: "./lib/vitest/index.mjs",
40
- });
41
- expect(packageJson.exports["./discovery"]).toEqual({
42
- types: "./lib/discovery/index.d.ts",
43
- default: "./lib/discovery/index.mjs",
44
- });
45
- expect(packageJson.exports["./regressions"]).toEqual({
46
- types: "./lib/regressions/index.d.ts",
47
- default: "./lib/regressions/index.mjs",
48
- });
49
- expect(fs.existsSync(path.join(rootDir, "lib", "index.d.ts"))).toBe(true);
50
- expect(fs.existsSync(path.join(rootDir, "lib", "config-api", "index.d.ts"))).toBe(true);
51
- expect(fs.existsSync(path.join(rootDir, "lib", "drizzle", "index.d.ts"))).toBe(true);
52
- expect(fs.existsSync(path.join(rootDir, "lib", "env", "index.d.ts"))).toBe(true);
53
- expect(fs.existsSync(path.join(rootDir, "lib", "playwright", "index.d.ts"))).toBe(true);
54
- expect(fs.existsSync(path.join(rootDir, "lib", "runtime", "index.d.ts"))).toBe(true);
55
- expect(fs.existsSync(path.join(rootDir, "lib", "vitest", "index.d.ts"))).toBe(true);
56
- expect(fs.existsSync(path.join(rootDir, "lib", "discovery", "index.d.ts"))).toBe(true);
57
- expect(fs.existsSync(path.join(rootDir, "lib", "regressions", "index.d.ts"))).toBe(true);
58
- });
59
- });
@@ -1,43 +0,0 @@
1
- import { describe, expect, it } from "vitest";
2
- import { defineConfig } from "./index.mjs";
3
-
4
- describe("testkit playwright defineConfig", () => {
5
- it("derives timeouts from the managed file-timeout env", () => {
6
- const config = defineConfig(
7
- {
8
- use: {
9
- trace: "on-first-retry",
10
- },
11
- },
12
- {
13
- env: {
14
- TESTKIT_INTERNAL_FILE_TIMEOUT_SECONDS: "45",
15
- },
16
- }
17
- );
18
-
19
- expect(config.timeout).toBe(45_000);
20
- expect(config.expect.timeout).toBe(45_000);
21
- expect(config.use.actionTimeout).toBe(45_000);
22
- expect(config.use.trace).toBe("on-first-retry");
23
- });
24
-
25
- it("disables webServer during managed runs and respects BASE_URL", () => {
26
- const config = defineConfig(
27
- {
28
- webServer: [{ command: "npm run dev" }],
29
- use: {},
30
- },
31
- {
32
- env: {
33
- TESTKIT_ACTIVE: "1",
34
- TESTKIT_MANAGED_SERVERS: "1",
35
- BASE_URL: "http://127.0.0.1:3000",
36
- },
37
- }
38
- );
39
-
40
- expect(config.webServer).toBeUndefined();
41
- expect(config.use.baseURL).toBe("http://127.0.0.1:3000");
42
- });
43
- });