@elench/testkit 0.1.83 → 0.1.85
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/lib/cli/agents/investigation-interpreter.mjs +320 -0
- package/lib/cli/agents/investigation-log.mjs +37 -0
- package/lib/cli/agents/providers/codex.mjs +1 -1
- package/lib/cli/presentation/tree-reporter.mjs +33 -1
- package/lib/cli/tui/run-session-app.mjs +73 -11
- package/lib/cli/tui/run-session-state.mjs +29 -5
- package/node_modules/@elench/next-analysis/package.json +1 -1
- package/node_modules/@elench/testkit-bridge/package.json +2 -2
- package/node_modules/@elench/testkit-protocol/package.json +1 -1
- package/node_modules/@elench/ts-analysis/package.json +1 -1
- package/package.json +7 -6
- package/lib/app/configs.test.mjs +0 -34
- package/lib/app/typecheck.test.mjs +0 -24
- package/lib/bundler/index.test.mjs +0 -164
- package/lib/cli/agents/investigation-context.test.mjs +0 -144
- package/lib/cli/agents/providers/claude.test.mjs +0 -95
- package/lib/cli/agents/providers/codex.test.mjs +0 -93
- package/lib/cli/args.test.mjs +0 -110
- package/lib/cli/command-helpers.test.mjs +0 -122
- package/lib/cli/commands/investigate.test.mjs +0 -83
- package/lib/cli/presentation/code-frames.test.mjs +0 -71
- package/lib/cli/presentation/events-reporter.test.mjs +0 -73
- package/lib/cli/presentation/run-reporter.test.mjs +0 -192
- package/lib/cli/presentation/summary-box.test.mjs +0 -60
- package/lib/cli/presentation/terminal-layout.test.mjs +0 -23
- package/lib/cli/presentation/tree-reporter.test.mjs +0 -166
- package/lib/cli/tui/run-session-app.test.mjs +0 -50
- package/lib/cli/tui/run-tree-state.test.mjs +0 -324
- package/lib/config/database.test.mjs +0 -29
- package/lib/config/discovery.test.mjs +0 -276
- package/lib/config/env.test.mjs +0 -40
- package/lib/config/index.test.mjs +0 -44
- package/lib/config/paths.test.mjs +0 -27
- package/lib/config/runtime.test.mjs +0 -82
- package/lib/config/skip-config.test.mjs +0 -63
- package/lib/config-api/index.test.mjs +0 -398
- package/lib/config-api/next-runtime-tsconfig.test.mjs +0 -58
- package/lib/coverage/backend-discovery.test.mjs +0 -61
- package/lib/coverage/evidence.test.mjs +0 -87
- package/lib/coverage/index.test.mjs +0 -715
- package/lib/coverage/routing.test.mjs +0 -36
- package/lib/coverage/shared.test.mjs +0 -72
- package/lib/database/fingerprint.test.mjs +0 -99
- package/lib/database/index.test.mjs +0 -95
- package/lib/database/naming.test.mjs +0 -39
- package/lib/database/state.test.mjs +0 -66
- package/lib/database/template-steps.test.mjs +0 -43
- package/lib/discovery/file-metadata.test.mjs +0 -51
- package/lib/discovery/index.test.mjs +0 -182
- package/lib/discovery/path-policy.test.mjs +0 -65
- package/lib/drizzle/index.test.mjs +0 -33
- package/lib/env/index.test.mjs +0 -82
- package/lib/history/index.test.mjs +0 -115
- package/lib/package.test.mjs +0 -59
- package/lib/playwright/index.test.mjs +0 -43
- package/lib/regressions/github.test.mjs +0 -324
- package/lib/regressions/index.test.mjs +0 -187
- package/lib/reporters/playwright.test.mjs +0 -167
- package/lib/runner/default-runtime-errors.test.mjs +0 -49
- package/lib/runner/execution-config.test.mjs +0 -67
- package/lib/runner/failure-details.test.mjs +0 -114
- package/lib/runner/formatting.test.mjs +0 -205
- package/lib/runner/metadata.test.mjs +0 -52
- package/lib/runner/planning.test.mjs +0 -371
- package/lib/runner/playwright-config.test.mjs +0 -78
- package/lib/runner/processes.test.mjs +0 -21
- package/lib/runner/regressions.test.mjs +0 -168
- package/lib/runner/reporting.test.mjs +0 -310
- package/lib/runner/results.test.mjs +0 -376
- package/lib/runner/runtime-manager.test.mjs +0 -252
- package/lib/runner/runtime-preparation.test.mjs +0 -141
- package/lib/runner/selection.test.mjs +0 -24
- package/lib/runner/setup-operations.test.mjs +0 -94
- package/lib/runner/state.test.mjs +0 -62
- package/lib/runner/suite-selection.test.mjs +0 -49
- package/lib/runner/template.test.mjs +0 -272
- package/lib/runtime-src/k6/http-checks.test.mjs +0 -120
- package/lib/runtime-src/k6/http.test.mjs +0 -205
- package/lib/runtime-src/shared/http-parsing.test.mjs +0 -69
- package/lib/shared/build-config.test.mjs +0 -132
- package/lib/shared/configured-steps.test.mjs +0 -102
- package/lib/shared/execution-schema.test.mjs +0 -26
- package/lib/shared/file-timeout.test.mjs +0 -64
- package/lib/shared/test-context.test.mjs +0 -43
- package/lib/timing/index.test.mjs +0 -64
- package/lib/toolchains/index.test.mjs +0 -168
- package/lib/vitest/index.test.mjs +0 -20
package/lib/cli/args.test.mjs
DELETED
|
@@ -1,110 +0,0 @@
|
|
|
1
|
-
import { describe, expect, it } from "vitest";
|
|
2
|
-
import {
|
|
3
|
-
parseFileTimeoutOption,
|
|
4
|
-
parseShardOption,
|
|
5
|
-
parseSuiteOption,
|
|
6
|
-
parseTypeOption,
|
|
7
|
-
parseWorkersOption,
|
|
8
|
-
resolveRequestedFiles,
|
|
9
|
-
resolveCliSelection,
|
|
10
|
-
} from "./args.mjs";
|
|
11
|
-
|
|
12
|
-
describe("cli-args", () => {
|
|
13
|
-
it("resolves a positional suite type", () => {
|
|
14
|
-
expect(
|
|
15
|
-
resolveCliSelection({
|
|
16
|
-
first: "int",
|
|
17
|
-
second: null,
|
|
18
|
-
third: null,
|
|
19
|
-
})
|
|
20
|
-
).toEqual({
|
|
21
|
-
dbAction: null,
|
|
22
|
-
lifecycle: null,
|
|
23
|
-
positionalType: "int",
|
|
24
|
-
});
|
|
25
|
-
});
|
|
26
|
-
|
|
27
|
-
it("resolves lifecycle commands", () => {
|
|
28
|
-
expect(
|
|
29
|
-
resolveCliSelection({
|
|
30
|
-
first: "status",
|
|
31
|
-
second: null,
|
|
32
|
-
third: null,
|
|
33
|
-
})
|
|
34
|
-
).toEqual({
|
|
35
|
-
dbAction: null,
|
|
36
|
-
lifecycle: "status",
|
|
37
|
-
positionalType: null,
|
|
38
|
-
});
|
|
39
|
-
});
|
|
40
|
-
|
|
41
|
-
it("resolves db subcommands", () => {
|
|
42
|
-
expect(
|
|
43
|
-
resolveCliSelection({
|
|
44
|
-
first: "db",
|
|
45
|
-
second: "snapshot",
|
|
46
|
-
third: "capture",
|
|
47
|
-
})
|
|
48
|
-
).toEqual({
|
|
49
|
-
dbAction: "snapshot-capture",
|
|
50
|
-
lifecycle: null,
|
|
51
|
-
positionalType: null,
|
|
52
|
-
});
|
|
53
|
-
});
|
|
54
|
-
|
|
55
|
-
it("rejects unknown positional arguments", () => {
|
|
56
|
-
expect(() =>
|
|
57
|
-
resolveCliSelection({
|
|
58
|
-
first: "mystery",
|
|
59
|
-
second: null,
|
|
60
|
-
third: null,
|
|
61
|
-
})
|
|
62
|
-
).toThrow('Unknown argument "mystery"');
|
|
63
|
-
});
|
|
64
|
-
|
|
65
|
-
it("parses types and suite selectors", () => {
|
|
66
|
-
expect(parseTypeOption(["e2e,scenario,dal"], "int")).toEqual([
|
|
67
|
-
"int",
|
|
68
|
-
"e2e",
|
|
69
|
-
"scenario",
|
|
70
|
-
"dal",
|
|
71
|
-
]);
|
|
72
|
-
expect(() => parseTypeOption(["all", "int"])).toThrow("cannot be combined");
|
|
73
|
-
|
|
74
|
-
expect(parseSuiteOption(["auth,scenario:journeys,dal:queries"])).toEqual([
|
|
75
|
-
{ kind: "plain", name: "auth", raw: "auth" },
|
|
76
|
-
{ kind: "typed", type: "scenario", name: "journeys", raw: "scenario:journeys" },
|
|
77
|
-
{ kind: "typed", type: "dal", name: "queries", raw: "dal:queries" },
|
|
78
|
-
]);
|
|
79
|
-
});
|
|
80
|
-
|
|
81
|
-
it("parses and validates execution options", () => {
|
|
82
|
-
expect(parseWorkersOption("3")).toBe(3);
|
|
83
|
-
expect(parseFileTimeoutOption("45")).toBe(45);
|
|
84
|
-
expect(() => parseWorkersOption("0")).toThrow("Invalid --workers value");
|
|
85
|
-
expect(() => parseFileTimeoutOption("0")).toThrow(
|
|
86
|
-
"Invalid --file-timeout-seconds value"
|
|
87
|
-
);
|
|
88
|
-
});
|
|
89
|
-
|
|
90
|
-
it("parses and validates shards", () => {
|
|
91
|
-
expect(parseShardOption("2/5")).toEqual({ index: 2, total: 5 });
|
|
92
|
-
expect(parseShardOption(null)).toBeNull();
|
|
93
|
-
expect(() => parseShardOption("2-of-5")).toThrow("Invalid --shard value");
|
|
94
|
-
expect(() => parseShardOption("3/2")).toThrow("Expected 1 <= i <= n");
|
|
95
|
-
});
|
|
96
|
-
|
|
97
|
-
it("normalizes requested file paths against the product directory", () => {
|
|
98
|
-
expect(
|
|
99
|
-
resolveRequestedFiles(
|
|
100
|
-
[
|
|
101
|
-
"/tmp/product/tests/api/integration/health.int.testkit.ts",
|
|
102
|
-
"./tests/api/integration/health.int.testkit.ts",
|
|
103
|
-
"product/tests/api/integration/health.int.testkit.ts",
|
|
104
|
-
],
|
|
105
|
-
"/tmp/product",
|
|
106
|
-
"/tmp"
|
|
107
|
-
)
|
|
108
|
-
).toEqual(["tests/api/integration/health.int.testkit.ts"]);
|
|
109
|
-
});
|
|
110
|
-
});
|
|
@@ -1,122 +0,0 @@
|
|
|
1
|
-
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
|
2
|
-
|
|
3
|
-
const loadManagedConfigsMock = vi.fn();
|
|
4
|
-
const runAllMock = vi.fn();
|
|
5
|
-
const createRunReporterMock = vi.fn();
|
|
6
|
-
const createTreeReporterMock = vi.fn();
|
|
7
|
-
const createRunEventsReporterMock = vi.fn();
|
|
8
|
-
|
|
9
|
-
vi.mock("../app/configs.mjs", () => ({
|
|
10
|
-
loadManagedConfigs: loadManagedConfigsMock,
|
|
11
|
-
}));
|
|
12
|
-
|
|
13
|
-
vi.mock("../runner/index.mjs", () => ({
|
|
14
|
-
runAll: runAllMock,
|
|
15
|
-
}));
|
|
16
|
-
|
|
17
|
-
vi.mock("./presentation/run-reporter.mjs", () => ({
|
|
18
|
-
createRunReporter: createRunReporterMock,
|
|
19
|
-
}));
|
|
20
|
-
|
|
21
|
-
vi.mock("./presentation/tree-reporter.mjs", () => ({
|
|
22
|
-
createTreeReporter: createTreeReporterMock,
|
|
23
|
-
}));
|
|
24
|
-
|
|
25
|
-
vi.mock("./presentation/events-reporter.mjs", () => ({
|
|
26
|
-
createRunEventsReporter: createRunEventsReporterMock,
|
|
27
|
-
}));
|
|
28
|
-
|
|
29
|
-
const originalIsTTY = process.stdout.isTTY;
|
|
30
|
-
const originalStdoutGetWindowSize = process.stdout.getWindowSize;
|
|
31
|
-
const originalStderrGetWindowSize = process.stderr.getWindowSize;
|
|
32
|
-
|
|
33
|
-
beforeEach(() => {
|
|
34
|
-
loadManagedConfigsMock.mockResolvedValue({
|
|
35
|
-
allConfigs: [{ name: "api", productDir: "/tmp/product" }],
|
|
36
|
-
configs: [{ name: "api", productDir: "/tmp/product" }],
|
|
37
|
-
});
|
|
38
|
-
runAllMock.mockResolvedValue({ ok: true });
|
|
39
|
-
createRunReporterMock.mockReturnValue({ outputMode: "compact" });
|
|
40
|
-
createRunEventsReporterMock.mockReturnValue({ outputMode: "events" });
|
|
41
|
-
createTreeReporterMock.mockReturnValue({
|
|
42
|
-
reporter: { outputMode: "compact" },
|
|
43
|
-
finalize: Promise.resolve(),
|
|
44
|
-
close: vi.fn(),
|
|
45
|
-
});
|
|
46
|
-
Object.defineProperty(process.stdout, "isTTY", {
|
|
47
|
-
configurable: true,
|
|
48
|
-
value: true,
|
|
49
|
-
});
|
|
50
|
-
process.stdout.getWindowSize = vi.fn(() => [100, 40]);
|
|
51
|
-
process.stderr.getWindowSize = vi.fn(() => [100, 40]);
|
|
52
|
-
});
|
|
53
|
-
|
|
54
|
-
afterEach(() => {
|
|
55
|
-
loadManagedConfigsMock.mockReset();
|
|
56
|
-
runAllMock.mockReset();
|
|
57
|
-
createRunReporterMock.mockReset();
|
|
58
|
-
createTreeReporterMock.mockReset();
|
|
59
|
-
createRunEventsReporterMock.mockReset();
|
|
60
|
-
Object.defineProperty(process.stdout, "isTTY", {
|
|
61
|
-
configurable: true,
|
|
62
|
-
value: originalIsTTY,
|
|
63
|
-
});
|
|
64
|
-
process.stdout.getWindowSize = originalStdoutGetWindowSize;
|
|
65
|
-
process.stderr.getWindowSize = originalStderrGetWindowSize;
|
|
66
|
-
});
|
|
67
|
-
|
|
68
|
-
describe("executeRunCommand", () => {
|
|
69
|
-
it("uses the tree reporter for compact TTY runs and awaits finalization", async () => {
|
|
70
|
-
let finalizeResolved = false;
|
|
71
|
-
createTreeReporterMock.mockReturnValueOnce({
|
|
72
|
-
reporter: { outputMode: "compact" },
|
|
73
|
-
finalize: Promise.resolve().then(() => {
|
|
74
|
-
finalizeResolved = true;
|
|
75
|
-
}),
|
|
76
|
-
close: vi.fn(),
|
|
77
|
-
});
|
|
78
|
-
|
|
79
|
-
const { executeRunCommand } = await import("./command-helpers.mjs");
|
|
80
|
-
const result = await executeRunCommand({ jsonEnabled: () => false }, {}, null);
|
|
81
|
-
|
|
82
|
-
expect(createTreeReporterMock).toHaveBeenCalledWith(
|
|
83
|
-
expect.objectContaining({ productDir: "/tmp/product" })
|
|
84
|
-
);
|
|
85
|
-
expect(runAllMock).toHaveBeenCalledWith(
|
|
86
|
-
expect.any(Array),
|
|
87
|
-
expect.any(Array),
|
|
88
|
-
expect.any(Array),
|
|
89
|
-
expect.objectContaining({ reporter: { outputMode: "compact" } }),
|
|
90
|
-
expect.any(Array)
|
|
91
|
-
);
|
|
92
|
-
expect(finalizeResolved).toBe(true);
|
|
93
|
-
expect(result.outputMode).toBe("compact");
|
|
94
|
-
});
|
|
95
|
-
|
|
96
|
-
it("uses the events reporter when requested", async () => {
|
|
97
|
-
const { executeRunCommand } = await import("./command-helpers.mjs");
|
|
98
|
-
await executeRunCommand(
|
|
99
|
-
{ jsonEnabled: () => false },
|
|
100
|
-
{ "output-mode": "events" },
|
|
101
|
-
null
|
|
102
|
-
);
|
|
103
|
-
|
|
104
|
-
expect(createRunEventsReporterMock).toHaveBeenCalled();
|
|
105
|
-
expect(createRunReporterMock).not.toHaveBeenCalled();
|
|
106
|
-
});
|
|
107
|
-
|
|
108
|
-
it("closes the tree reporter when runner.runAll throws", async () => {
|
|
109
|
-
const close = vi.fn();
|
|
110
|
-
createTreeReporterMock.mockReturnValueOnce({
|
|
111
|
-
reporter: { outputMode: "compact" },
|
|
112
|
-
finalize: Promise.resolve(),
|
|
113
|
-
close,
|
|
114
|
-
});
|
|
115
|
-
runAllMock.mockRejectedValueOnce(new Error("boom"));
|
|
116
|
-
|
|
117
|
-
const { executeRunCommand } = await import("./command-helpers.mjs");
|
|
118
|
-
|
|
119
|
-
await expect(executeRunCommand({ jsonEnabled: () => false }, {}, null)).rejects.toThrow("boom");
|
|
120
|
-
expect(close).toHaveBeenCalled();
|
|
121
|
-
});
|
|
122
|
-
});
|
|
@@ -1,83 +0,0 @@
|
|
|
1
|
-
import { afterEach, describe, expect, it, vi } from "vitest";
|
|
2
|
-
|
|
3
|
-
const loadCurrentRunArtifactMock = vi.fn();
|
|
4
|
-
const resolveFileSubjectMock = vi.fn();
|
|
5
|
-
const startHostedInvestigationMock = vi.fn();
|
|
6
|
-
const runInteractiveInvestigationMock = vi.fn();
|
|
7
|
-
|
|
8
|
-
vi.mock("../viewer.mjs", () => ({
|
|
9
|
-
loadCurrentRunArtifact: loadCurrentRunArtifactMock,
|
|
10
|
-
resolveFileSubject: resolveFileSubjectMock,
|
|
11
|
-
}));
|
|
12
|
-
|
|
13
|
-
vi.mock("../agents/investigate.mjs", () => ({
|
|
14
|
-
startHostedInvestigation: startHostedInvestigationMock,
|
|
15
|
-
runInteractiveInvestigation: runInteractiveInvestigationMock,
|
|
16
|
-
}));
|
|
17
|
-
|
|
18
|
-
afterEach(() => {
|
|
19
|
-
loadCurrentRunArtifactMock.mockReset();
|
|
20
|
-
resolveFileSubjectMock.mockReset();
|
|
21
|
-
startHostedInvestigationMock.mockReset();
|
|
22
|
-
runInteractiveInvestigationMock.mockReset();
|
|
23
|
-
});
|
|
24
|
-
|
|
25
|
-
function seedSubject() {
|
|
26
|
-
loadCurrentRunArtifactMock.mockReturnValue({});
|
|
27
|
-
resolveFileSubjectMock.mockReturnValue({
|
|
28
|
-
service: { name: "api" },
|
|
29
|
-
suite: { name: "users", type: "integration" },
|
|
30
|
-
file: { path: "tests/api/users.int.testkit.ts" },
|
|
31
|
-
});
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
describe("InvestigateCommand", () => {
|
|
35
|
-
it("runs hosted investigation and returns the final text", async () => {
|
|
36
|
-
seedSubject();
|
|
37
|
-
startHostedInvestigationMock.mockReturnValue({
|
|
38
|
-
completion: Promise.resolve({
|
|
39
|
-
provider: "codex",
|
|
40
|
-
exitCode: 0,
|
|
41
|
-
finalText: "Likely root cause.",
|
|
42
|
-
}),
|
|
43
|
-
});
|
|
44
|
-
|
|
45
|
-
const { default: InvestigateCommand } = await import("./investigate.mjs");
|
|
46
|
-
const result = await InvestigateCommand.run(["--json"]);
|
|
47
|
-
|
|
48
|
-
expect(startHostedInvestigationMock).toHaveBeenCalledWith(
|
|
49
|
-
expect.objectContaining({
|
|
50
|
-
productDir: process.cwd(),
|
|
51
|
-
serviceName: "api",
|
|
52
|
-
filePath: "tests/api/users.int.testkit.ts",
|
|
53
|
-
})
|
|
54
|
-
);
|
|
55
|
-
expect(result).toMatchObject({
|
|
56
|
-
provider: "codex",
|
|
57
|
-
finalText: "Likely root cause.",
|
|
58
|
-
file: "tests/api/users.int.testkit.ts",
|
|
59
|
-
service: "api",
|
|
60
|
-
});
|
|
61
|
-
});
|
|
62
|
-
|
|
63
|
-
it("runs handoff investigation when requested", async () => {
|
|
64
|
-
seedSubject();
|
|
65
|
-
runInteractiveInvestigationMock.mockResolvedValue({
|
|
66
|
-
provider: "claude",
|
|
67
|
-
exitCode: 0,
|
|
68
|
-
});
|
|
69
|
-
|
|
70
|
-
const { default: InvestigateCommand } = await import("./investigate.mjs");
|
|
71
|
-
const result = await InvestigateCommand.run(["--json", "--handoff", "--provider", "claude"]);
|
|
72
|
-
|
|
73
|
-
expect(runInteractiveInvestigationMock).toHaveBeenCalledWith(
|
|
74
|
-
expect.objectContaining({
|
|
75
|
-
productDir: process.cwd(),
|
|
76
|
-
serviceName: "api",
|
|
77
|
-
filePath: "tests/api/users.int.testkit.ts",
|
|
78
|
-
provider: "claude",
|
|
79
|
-
})
|
|
80
|
-
);
|
|
81
|
-
expect(result).toMatchObject({ provider: "claude", exitCode: 0 });
|
|
82
|
-
});
|
|
83
|
-
});
|
|
@@ -1,71 +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 { findFailureLocation, formatLocation, renderCodeFrame } from "./code-frames.mjs";
|
|
6
|
-
|
|
7
|
-
const cleanups = [];
|
|
8
|
-
|
|
9
|
-
afterEach(() => {
|
|
10
|
-
while (cleanups.length > 0) {
|
|
11
|
-
cleanups.pop()();
|
|
12
|
-
}
|
|
13
|
-
});
|
|
14
|
-
|
|
15
|
-
describe("code frame presentation", () => {
|
|
16
|
-
it("finds a location from structured failure metadata", () => {
|
|
17
|
-
expect(
|
|
18
|
-
findFailureLocation({
|
|
19
|
-
location: {
|
|
20
|
-
path: "/tmp/example.ts",
|
|
21
|
-
line: 10,
|
|
22
|
-
column: 2,
|
|
23
|
-
},
|
|
24
|
-
})
|
|
25
|
-
).toEqual({
|
|
26
|
-
path: "/tmp/example.ts",
|
|
27
|
-
line: 10,
|
|
28
|
-
column: 2,
|
|
29
|
-
});
|
|
30
|
-
});
|
|
31
|
-
|
|
32
|
-
it("renders a code frame for a local file", () => {
|
|
33
|
-
const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), "testkit-code-frame-"));
|
|
34
|
-
cleanups.push(() => fs.rmSync(tempDir, { recursive: true, force: true }));
|
|
35
|
-
const filePath = path.join(tempDir, "example.ts");
|
|
36
|
-
fs.writeFileSync(
|
|
37
|
-
filePath,
|
|
38
|
-
[
|
|
39
|
-
"export function run() {",
|
|
40
|
-
" const value = 1;",
|
|
41
|
-
" return value + missing;",
|
|
42
|
-
"}",
|
|
43
|
-
].join("\n")
|
|
44
|
-
);
|
|
45
|
-
|
|
46
|
-
const lines = renderCodeFrame(
|
|
47
|
-
{
|
|
48
|
-
path: filePath,
|
|
49
|
-
line: 3,
|
|
50
|
-
column: 18,
|
|
51
|
-
},
|
|
52
|
-
{ cwd: tempDir }
|
|
53
|
-
);
|
|
54
|
-
|
|
55
|
-
expect(lines.join("\n")).toContain("example.ts:3:18");
|
|
56
|
-
expect(lines.join("\n")).toContain("return value + missing;");
|
|
57
|
-
});
|
|
58
|
-
|
|
59
|
-
it("formats locations relative to the cwd", () => {
|
|
60
|
-
expect(
|
|
61
|
-
formatLocation(
|
|
62
|
-
{
|
|
63
|
-
path: "/tmp/example.ts",
|
|
64
|
-
line: 7,
|
|
65
|
-
column: 3,
|
|
66
|
-
},
|
|
67
|
-
"/tmp"
|
|
68
|
-
)
|
|
69
|
-
).toBe("example.ts:7:3");
|
|
70
|
-
});
|
|
71
|
-
});
|
|
@@ -1,73 +0,0 @@
|
|
|
1
|
-
import { Writable } from "stream";
|
|
2
|
-
import { describe, expect, it } from "vitest";
|
|
3
|
-
import { createRunEventsReporter } from "./events-reporter.mjs";
|
|
4
|
-
|
|
5
|
-
function createCapture() {
|
|
6
|
-
let output = "";
|
|
7
|
-
const stream = new Writable({
|
|
8
|
-
write(chunk, _encoding, callback) {
|
|
9
|
-
output += chunk.toString();
|
|
10
|
-
callback();
|
|
11
|
-
},
|
|
12
|
-
});
|
|
13
|
-
return {
|
|
14
|
-
stream,
|
|
15
|
-
lines() {
|
|
16
|
-
return output.trim().split("\n").filter(Boolean).map((line) => JSON.parse(line));
|
|
17
|
-
},
|
|
18
|
-
};
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
describe("events reporter", () => {
|
|
22
|
-
it("emits structured run lifecycle events", () => {
|
|
23
|
-
const capture = createCapture();
|
|
24
|
-
const reporter = createRunEventsReporter({ stdout: capture.stream });
|
|
25
|
-
|
|
26
|
-
reporter.setTotalFileCount(2);
|
|
27
|
-
reporter.taskStarted({
|
|
28
|
-
serviceName: "api",
|
|
29
|
-
file: "tests/api/users.int.testkit.ts",
|
|
30
|
-
suiteName: "users",
|
|
31
|
-
type: "integration",
|
|
32
|
-
framework: "k6",
|
|
33
|
-
});
|
|
34
|
-
reporter.taskFinished(
|
|
35
|
-
{
|
|
36
|
-
serviceName: "api",
|
|
37
|
-
file: "tests/api/users.int.testkit.ts",
|
|
38
|
-
suiteName: "users",
|
|
39
|
-
type: "integration",
|
|
40
|
-
framework: "k6",
|
|
41
|
-
},
|
|
42
|
-
{ failed: true, error: "status 500" }
|
|
43
|
-
);
|
|
44
|
-
reporter.runSummary([{ name: "api", failed: true }], 1200, null);
|
|
45
|
-
|
|
46
|
-
expect(capture.lines()).toEqual([
|
|
47
|
-
{ type: "run.total_files", total: 2 },
|
|
48
|
-
{
|
|
49
|
-
type: "run.task_started",
|
|
50
|
-
service: "api",
|
|
51
|
-
file: "tests/api/users.int.testkit.ts",
|
|
52
|
-
suite: "users",
|
|
53
|
-
type: "integration",
|
|
54
|
-
framework: "k6",
|
|
55
|
-
},
|
|
56
|
-
{
|
|
57
|
-
type: "run.task_finished",
|
|
58
|
-
service: "api",
|
|
59
|
-
file: "tests/api/users.int.testkit.ts",
|
|
60
|
-
suite: "users",
|
|
61
|
-
type: "integration",
|
|
62
|
-
framework: "k6",
|
|
63
|
-
outcome: { failed: true, error: "status 500" },
|
|
64
|
-
},
|
|
65
|
-
{
|
|
66
|
-
type: "run.summary",
|
|
67
|
-
results: [{ name: "api", failed: true }],
|
|
68
|
-
durationMs: 1200,
|
|
69
|
-
regressionReport: null,
|
|
70
|
-
},
|
|
71
|
-
]);
|
|
72
|
-
});
|
|
73
|
-
});
|
|
@@ -1,192 +0,0 @@
|
|
|
1
|
-
import { Writable } from "stream";
|
|
2
|
-
import figures from "figures";
|
|
3
|
-
import { describe, expect, it } from "vitest";
|
|
4
|
-
import { createRunReporter } from "./run-reporter.mjs";
|
|
5
|
-
|
|
6
|
-
function createCapture(columns = 100) {
|
|
7
|
-
let stdout = "";
|
|
8
|
-
const stream = new Writable({
|
|
9
|
-
write(chunk, _encoding, callback) {
|
|
10
|
-
stdout += chunk.toString();
|
|
11
|
-
callback();
|
|
12
|
-
},
|
|
13
|
-
});
|
|
14
|
-
stream.columns = columns;
|
|
15
|
-
return {
|
|
16
|
-
stream,
|
|
17
|
-
read() {
|
|
18
|
-
return stdout;
|
|
19
|
-
},
|
|
20
|
-
};
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
describe("run reporter setup output", () => {
|
|
24
|
-
it("prints concise high-level setup summaries in compact mode", () => {
|
|
25
|
-
const capture = createCapture();
|
|
26
|
-
const reporter = createRunReporter({
|
|
27
|
-
outputMode: "compact",
|
|
28
|
-
stdout: capture.stream,
|
|
29
|
-
});
|
|
30
|
-
|
|
31
|
-
reporter.setupOperationFinished({
|
|
32
|
-
serviceName: "api",
|
|
33
|
-
stage: "template",
|
|
34
|
-
kind: "database-template",
|
|
35
|
-
summary: "template rebuild",
|
|
36
|
-
status: "passed",
|
|
37
|
-
durationMs: 8_000,
|
|
38
|
-
});
|
|
39
|
-
|
|
40
|
-
expect(capture.read()).toContain(`${figures.play} RUN SETUP api template rebuild`);
|
|
41
|
-
expect(capture.read()).toContain("8s");
|
|
42
|
-
});
|
|
43
|
-
|
|
44
|
-
it("does not print low-level setup steps in compact mode", () => {
|
|
45
|
-
const capture = createCapture();
|
|
46
|
-
const reporter = createRunReporter({
|
|
47
|
-
outputMode: "compact",
|
|
48
|
-
stdout: capture.stream,
|
|
49
|
-
});
|
|
50
|
-
|
|
51
|
-
reporter.setupOperationFinished({
|
|
52
|
-
serviceName: "api",
|
|
53
|
-
stage: "template:migrate:api:1",
|
|
54
|
-
kind: "setup-step",
|
|
55
|
-
summary: "sql-file: db/schema.sql",
|
|
56
|
-
status: "passed",
|
|
57
|
-
durationMs: 8_000,
|
|
58
|
-
});
|
|
59
|
-
|
|
60
|
-
expect(capture.read()).toBe("");
|
|
61
|
-
});
|
|
62
|
-
|
|
63
|
-
it("prints concise setup failures", () => {
|
|
64
|
-
const capture = createCapture();
|
|
65
|
-
const reporter = createRunReporter({
|
|
66
|
-
outputMode: "compact",
|
|
67
|
-
stdout: capture.stream,
|
|
68
|
-
});
|
|
69
|
-
|
|
70
|
-
reporter.setupOperationFinished({
|
|
71
|
-
serviceName: "api",
|
|
72
|
-
stage: "runtime:prepare",
|
|
73
|
-
kind: "runtime-prepare",
|
|
74
|
-
summary: "runtime prepare",
|
|
75
|
-
status: "failed",
|
|
76
|
-
durationMs: 1_200,
|
|
77
|
-
error: "Command failed with exit code 1: node scripts/fail-prepare.mjs",
|
|
78
|
-
});
|
|
79
|
-
|
|
80
|
-
expect(capture.read()).toContain(`${figures.cross} FAIL SETUP api runtime:prepare`);
|
|
81
|
-
expect(capture.read()).toContain("Command failed with exit code 1");
|
|
82
|
-
});
|
|
83
|
-
});
|
|
84
|
-
|
|
85
|
-
describe("run reporter task output", () => {
|
|
86
|
-
it("prints failure details on lines beneath the failed file result", () => {
|
|
87
|
-
const capture = createCapture(88);
|
|
88
|
-
const reporter = createRunReporter({
|
|
89
|
-
outputMode: "compact",
|
|
90
|
-
stdout: capture.stream,
|
|
91
|
-
});
|
|
92
|
-
|
|
93
|
-
reporter.setTotalFileCount(3);
|
|
94
|
-
reporter.taskFinished(
|
|
95
|
-
{
|
|
96
|
-
serviceName: "api",
|
|
97
|
-
type: "integration",
|
|
98
|
-
framework: "k6",
|
|
99
|
-
file: "__testkit__/health/health.int.testkit.ts",
|
|
100
|
-
},
|
|
101
|
-
{
|
|
102
|
-
status: "failed",
|
|
103
|
-
failed: true,
|
|
104
|
-
durationMs: 4_000,
|
|
105
|
-
error: "Default runtime thresholds failed: checks(rate==1.0)",
|
|
106
|
-
failureDetails: [
|
|
107
|
-
{
|
|
108
|
-
kind: "http-assertion",
|
|
109
|
-
title: "status is 200",
|
|
110
|
-
message: "GET /health expected 200, got 404",
|
|
111
|
-
request: {
|
|
112
|
-
method: "GET",
|
|
113
|
-
path: "/health",
|
|
114
|
-
requestId: "req-1",
|
|
115
|
-
},
|
|
116
|
-
response: {
|
|
117
|
-
status: 404,
|
|
118
|
-
bodyPreview: '{"error":"route not found"}',
|
|
119
|
-
},
|
|
120
|
-
},
|
|
121
|
-
],
|
|
122
|
-
}
|
|
123
|
-
);
|
|
124
|
-
|
|
125
|
-
const lines = capture.read().trimEnd().split("\n");
|
|
126
|
-
expect(lines[0]).toContain("[1/3]");
|
|
127
|
-
expect(lines[0]).toContain(`${figures.cross} FAIL api int __testkit__/health/health.int.testkit.ts 4s`);
|
|
128
|
-
expect(lines[1]).toBe(" GET /health expected 200, got 404");
|
|
129
|
-
expect(lines[2]).toContain(' response: {"error":"route not found"}');
|
|
130
|
-
expect(lines[3]).toBe(" regression: new");
|
|
131
|
-
expect(lines[4]).toBe(" logs: requestId=req-1");
|
|
132
|
-
});
|
|
133
|
-
|
|
134
|
-
it("renders a compact aggregate summary box", () => {
|
|
135
|
-
const capture = createCapture(72);
|
|
136
|
-
const reporter = createRunReporter({
|
|
137
|
-
outputMode: "compact",
|
|
138
|
-
stdout: capture.stream,
|
|
139
|
-
});
|
|
140
|
-
|
|
141
|
-
reporter.runSummary(
|
|
142
|
-
[
|
|
143
|
-
{
|
|
144
|
-
name: "api",
|
|
145
|
-
skipped: false,
|
|
146
|
-
failed: true,
|
|
147
|
-
suiteCount: 1,
|
|
148
|
-
completedSuiteCount: 1,
|
|
149
|
-
skippedSuiteCount: 0,
|
|
150
|
-
failedSuiteCount: 1,
|
|
151
|
-
totalFileCount: 3,
|
|
152
|
-
passedFileCount: 2,
|
|
153
|
-
failedFileCount: 1,
|
|
154
|
-
skippedFileCount: 0,
|
|
155
|
-
notRunFileCount: 0,
|
|
156
|
-
durationMs: 10_000,
|
|
157
|
-
suites: [],
|
|
158
|
-
errors: [],
|
|
159
|
-
},
|
|
160
|
-
],
|
|
161
|
-
20_000,
|
|
162
|
-
{
|
|
163
|
-
summary: {
|
|
164
|
-
newRegressions: 1,
|
|
165
|
-
knownRegressions: 2,
|
|
166
|
-
fixedKnownRegressions: 3,
|
|
167
|
-
catalogStale: 4,
|
|
168
|
-
catalogSyncUnavailable: false,
|
|
169
|
-
usedStaleCache: true,
|
|
170
|
-
},
|
|
171
|
-
}
|
|
172
|
-
);
|
|
173
|
-
|
|
174
|
-
const output = capture.read();
|
|
175
|
-
expect(output).toContain("┌");
|
|
176
|
-
expect(output).toContain("│ Result");
|
|
177
|
-
expect(output).toContain("FAILED");
|
|
178
|
-
expect(output).toContain("│ Passed");
|
|
179
|
-
expect(output).toContain("│ Failed");
|
|
180
|
-
expect(output).toContain("│ Skipped");
|
|
181
|
-
expect(output).toContain("│ Not run");
|
|
182
|
-
expect(output).toContain("│ Files");
|
|
183
|
-
expect(output).toContain("│ Duration");
|
|
184
|
-
expect(output).toContain("│ New regressions");
|
|
185
|
-
expect(output).toContain("│ Known regressions");
|
|
186
|
-
expect(output).toContain("│ Fixed known");
|
|
187
|
-
expect(output).toContain("│ Catalog stale");
|
|
188
|
-
expect(output).toContain("│ Catalog sync");
|
|
189
|
-
expect(output).not.toContain("Failures:");
|
|
190
|
-
expect(output).not.toContain("Runtime Errors:");
|
|
191
|
-
});
|
|
192
|
-
});
|