@elench/testkit 0.1.80 → 0.1.82

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 (54) hide show
  1. package/README.md +78 -56
  2. package/lib/cli/args.mjs +2 -14
  3. package/lib/cli/args.test.mjs +1 -17
  4. package/lib/cli/command-helpers.mjs +1 -20
  5. package/lib/cli/entrypoint.mjs +0 -4
  6. package/lib/cli/presentation/colors.mjs +1 -1
  7. package/lib/cli/presentation/failure-presentation.mjs +4 -4
  8. package/lib/cli/presentation/run-reporter.mjs +23 -9
  9. package/lib/cli/presentation/run-reporter.test.mjs +12 -6
  10. package/lib/cli/presentation/summary-box.test.mjs +4 -4
  11. package/lib/cli/viewer.mjs +18 -19
  12. package/lib/config/index.mjs +6 -6
  13. package/lib/config/runtime.mjs +8 -8
  14. package/lib/config-api/auth-fixtures.mjs +762 -0
  15. package/lib/config-api/index.d.ts +96 -112
  16. package/lib/config-api/index.mjs +22 -12
  17. package/lib/config-api/index.test.mjs +61 -222
  18. package/lib/index.d.ts +29 -9
  19. package/lib/package.test.mjs +4 -4
  20. package/lib/{known-failures → regressions}/github.mjs +36 -78
  21. package/lib/regressions/github.test.mjs +324 -0
  22. package/lib/regressions/index.d.ts +189 -0
  23. package/lib/{known-failures → regressions}/index.mjs +90 -93
  24. package/lib/{known-failures → regressions}/index.test.mjs +37 -48
  25. package/lib/runner/formatting.mjs +49 -34
  26. package/lib/runner/formatting.test.mjs +16 -15
  27. package/lib/runner/metadata.mjs +1 -1
  28. package/lib/runner/orchestrator.mjs +7 -9
  29. package/lib/runner/regressions.mjs +304 -0
  30. package/lib/runner/{triage.test.mjs → regressions.test.mjs} +50 -36
  31. package/lib/runner/reporting.mjs +2 -2
  32. package/lib/runner/reporting.test.mjs +2 -2
  33. package/lib/runner/run-finalization.mjs +18 -30
  34. package/lib/runner/template-steps.mjs +2 -2
  35. package/lib/runtime/index.d.ts +50 -33
  36. package/lib/runtime/index.mjs +0 -1
  37. package/lib/runtime-src/k6/http-suite-runtime.js +147 -0
  38. package/lib/runtime-src/k6/http.js +80 -41
  39. package/lib/runtime-src/k6/scenario-suite.js +13 -110
  40. package/lib/runtime-src/k6/suite.js +13 -107
  41. package/node_modules/@elench/next-analysis/package.json +1 -1
  42. package/node_modules/@elench/testkit-bridge/package.json +2 -2
  43. package/node_modules/@elench/testkit-protocol/package.json +1 -1
  44. package/node_modules/@elench/ts-analysis/package.json +1 -1
  45. package/package.json +8 -8
  46. package/lib/cli/commands/known-failures/render.mjs +0 -19
  47. package/lib/cli/commands/known-failures/validate.mjs +0 -20
  48. package/lib/cli/known-failures.mjs +0 -164
  49. package/lib/config-api/profiles.mjs +0 -640
  50. package/lib/known-failures/github.test.mjs +0 -512
  51. package/lib/known-failures/index.d.ts +0 -192
  52. package/lib/runner/triage.mjs +0 -221
  53. /package/lib/{known-failures → regressions}/github-cache.mjs +0 -0
  54. /package/lib/{known-failures → regressions}/github-transport.mjs +0 -0
@@ -7,16 +7,21 @@ import {
7
7
  startFailureCollection,
8
8
  } from "./checks.js";
9
9
  import {
10
- createHttpClient,
11
10
  emitHttpTraceCollectionArtifact,
12
- getEnv,
13
11
  startHttpTraceCollection,
14
12
  } from "./http.js";
15
13
  import {
16
14
  clearRuntimeContext,
17
15
  registerRuntimeContext,
18
- resolveHttpProfile,
19
16
  } from "../../config-api/runtime.mjs";
17
+ import {
18
+ buildRuntimeExceptionDetail,
19
+ createSuiteActors,
20
+ formatFatalSuiteError,
21
+ mergeProfileConfig,
22
+ normalizeSuiteArgs,
23
+ resolveRuntimeConfig,
24
+ } from "./http-suite-runtime.js";
20
25
 
21
26
  export function defineHttpSuite(configOrRun, maybeRun) {
22
27
  const { config, run } = normalizeSuiteArgs(configOrRun, maybeRun);
@@ -44,18 +49,18 @@ export function defineHttpSuite(configOrRun, maybeRun) {
44
49
  }
45
50
  },
46
51
  exec(setupData) {
47
- const resolved = resolveRuntimeConfig(config);
52
+ const resolved = resolveRuntimeConfig(config, setupData);
53
+ const actorContext = createSuiteActors(setupData, resolved.client, resolved.profileMeta);
48
54
  startFailureCollection("exec");
49
55
  startHttpTraceCollection("exec");
50
56
  try {
51
57
  registerRuntimeContext({ env: resolved.env, http: resolved.client.rawHttp || null });
52
58
  return run({
59
+ actor: actorContext.actor,
60
+ actors: actorContext.actors,
53
61
  env: resolved.env,
54
- req: resolved.client.request,
62
+ req: resolved.client,
55
63
  rawReq: resolved.client.raw,
56
- getWithHeaders: resolved.client.getWithHeaders,
57
- setupData,
58
- session: setupData,
59
64
  });
60
65
  } catch (error) {
61
66
  recordFailureDetail(buildRuntimeExceptionDetail("exec", error));
@@ -69,102 +74,3 @@ export function defineHttpSuite(configOrRun, maybeRun) {
69
74
  },
70
75
  };
71
76
  }
72
-
73
- function normalizeSuiteArgs(configOrRun, maybeRun) {
74
- if (typeof configOrRun === "function") {
75
- return { config: {}, run: configOrRun };
76
- }
77
- if (typeof maybeRun !== "function") {
78
- throw new Error("suite factory requires a run callback");
79
- }
80
- return { config: configOrRun || {}, run: maybeRun };
81
- }
82
-
83
- function callHeaders(builder, setupData, env) {
84
- if (typeof builder !== "function") return {};
85
- return builder(setupData, { env }) || {};
86
- }
87
-
88
- function mergeProfileConfig(config) {
89
- if (!config?.profile) return config || {};
90
-
91
- const profile = resolveHttpProfile(config.profile) || {};
92
- return {
93
- ...profile,
94
- ...config,
95
- auth: config.auth ?? profile.auth ?? null,
96
- headers: config.headers ?? profile.headers,
97
- rawHeaders: config.rawHeaders ?? profile.rawHeaders,
98
- options: config.options ?? profile.options,
99
- env: config.env ?? profile.env,
100
- };
101
- }
102
-
103
- function resolveRuntimeConfig(config) {
104
- const resolvedConfig = mergeProfileConfig(config);
105
- const env = {
106
- ...(resolvedConfig.env || getEnv()),
107
- rawEnv: __ENV,
108
- };
109
- const auth = resolvedConfig.auth || null;
110
- const client = createHttpClient({
111
- baseUrl: env.BASE,
112
- routeHeaders: env.routeParams,
113
- getHeaders(setupData) {
114
- return {
115
- ...callHeaders(auth?.headers, setupData, env),
116
- ...callHeaders(resolvedConfig.headers, setupData, env),
117
- };
118
- },
119
- getRawHeaders(setupData) {
120
- return callHeaders(resolvedConfig.rawHeaders, setupData, env);
121
- },
122
- });
123
-
124
- return {
125
- resolvedConfig,
126
- env,
127
- auth,
128
- client,
129
- };
130
- }
131
-
132
- function formatFatalSuiteError(phase, error) {
133
- if (error instanceof Error) {
134
- return `Uncaught testkit suite error during ${phase}: ${error.message}`;
135
- }
136
- return `Uncaught testkit suite error during ${phase}: ${String(error)}`;
137
- }
138
-
139
- function buildRuntimeExceptionDetail(phase, error) {
140
- const message =
141
- error instanceof Error ? error.message : String(error);
142
- const stack = error instanceof Error && typeof error.stack === "string" ? error.stack : "";
143
- const location = extractLocationFromStack(stack);
144
- return {
145
- kind: "runtime-exception",
146
- key: location ? `${location.path}:${location.line}:${location.column}` : `runtime-exception:${phase}:${message}`,
147
- title: "Uncaught runtime exception",
148
- message: `Uncaught testkit suite error during ${phase}: ${message}`,
149
- location,
150
- stack,
151
- };
152
- }
153
-
154
- function extractLocationFromStack(stack) {
155
- if (!stack) return null;
156
- const matches = [...String(stack).matchAll(/(file:\/\/[^\s)]+|\/[^\s):]+):(\d+):(\d+)/g)].map((match) => ({
157
- path: normalizeStackPath(match[1]),
158
- line: Number(match[2]),
159
- column: Number(match[3]),
160
- }));
161
- return matches[0] || null;
162
- }
163
-
164
- function normalizeStackPath(rawPath) {
165
- if (typeof rawPath !== "string") return rawPath;
166
- if (rawPath.startsWith("file://")) {
167
- return rawPath.slice("file://".length);
168
- }
169
- return rawPath;
170
- }
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@elench/next-analysis",
3
- "version": "0.1.80",
3
+ "version": "0.1.82",
4
4
  "description": "SWC-backed Next.js source analysis primitives for Erench tools",
5
5
  "type": "module",
6
6
  "exports": {
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@elench/testkit-bridge",
3
- "version": "0.1.80",
3
+ "version": "0.1.82",
4
4
  "description": "Browser bridge helpers for testkit",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -22,7 +22,7 @@
22
22
  "typecheck": "tsc -p tsconfig.json --noEmit"
23
23
  },
24
24
  "dependencies": {
25
- "@elench/testkit-protocol": "0.1.80"
25
+ "@elench/testkit-protocol": "0.1.82"
26
26
  },
27
27
  "private": false
28
28
  }
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@elench/testkit-protocol",
3
- "version": "0.1.80",
3
+ "version": "0.1.82",
4
4
  "description": "Shared browser protocol for testkit bridge and extension consumers",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@elench/ts-analysis",
3
- "version": "0.1.80",
3
+ "version": "0.1.82",
4
4
  "description": "TypeScript compiler-backed source analysis primitives for Erench tools",
5
5
  "type": "module",
6
6
  "exports": {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@elench/testkit",
3
- "version": "0.1.80",
3
+ "version": "0.1.82",
4
4
  "description": "CLI for discovering and running local HTTP, DAL, and Playwright test suites",
5
5
  "type": "module",
6
6
  "workspaces": [
@@ -40,9 +40,9 @@
40
40
  "types": "./lib/discovery/index.d.ts",
41
41
  "default": "./lib/discovery/index.mjs"
42
42
  },
43
- "./known-failures": {
44
- "types": "./lib/known-failures/index.d.ts",
45
- "default": "./lib/known-failures/index.mjs"
43
+ "./regressions": {
44
+ "types": "./lib/regressions/index.d.ts",
45
+ "default": "./lib/regressions/index.mjs"
46
46
  },
47
47
  "./package.json": "./package.json"
48
48
  },
@@ -81,10 +81,10 @@
81
81
  },
82
82
  "dependencies": {
83
83
  "@babel/code-frame": "^7.29.0",
84
- "@elench/next-analysis": "0.1.80",
85
- "@elench/testkit-bridge": "0.1.80",
86
- "@elench/testkit-protocol": "0.1.80",
87
- "@elench/ts-analysis": "0.1.80",
84
+ "@elench/next-analysis": "0.1.82",
85
+ "@elench/testkit-bridge": "0.1.82",
86
+ "@elench/testkit-protocol": "0.1.82",
87
+ "@elench/ts-analysis": "0.1.82",
88
88
  "@oclif/core": "^4.10.6",
89
89
  "esbuild": "^0.25.11",
90
90
  "execa": "^9.5.0",
@@ -1,19 +0,0 @@
1
- import { Command } from "@oclif/core";
2
- import { makeKnownFailuresFlags } from "../../command-helpers.mjs";
3
- import { runKnownFailuresRenderCommand } from "../../known-failures.mjs";
4
-
5
- export default class KnownFailuresRenderCommand extends Command {
6
- static summary = "Render known failures markdown";
7
-
8
- static enableJsonFlag = true;
9
-
10
- static flags = makeKnownFailuresFlags();
11
-
12
- async run() {
13
- const { flags } = await this.parse(KnownFailuresRenderCommand);
14
- await runKnownFailuresRenderCommand({
15
- ...flags,
16
- });
17
- return { ok: true };
18
- }
19
- }
@@ -1,20 +0,0 @@
1
- import { Command } from "@oclif/core";
2
- import { makeKnownFailuresFlags } from "../../command-helpers.mjs";
3
- import { runKnownFailuresValidateCommand } from "../../known-failures.mjs";
4
-
5
- export default class KnownFailuresValidateCommand extends Command {
6
- static summary = "Validate known failures against the latest status artifact";
7
-
8
- static enableJsonFlag = true;
9
-
10
- static flags = makeKnownFailuresFlags();
11
-
12
- async run() {
13
- const { flags } = await this.parse(KnownFailuresValidateCommand);
14
- await runKnownFailuresValidateCommand({
15
- ...flags,
16
- issueMode: flags["issue-mode"] || null,
17
- });
18
- return { ok: true };
19
- }
20
- }
@@ -1,164 +0,0 @@
1
- import fs from "fs";
2
- import path from "path";
3
- import { resolveProductDir } from "../config/index.mjs";
4
- import { loadTestkitConfig } from "../config/config-loader.mjs";
5
- import {
6
- buildKnownFailureIssueValidationSummaryLines,
7
- normalizeKnownFailureIssueValidationConfig,
8
- shouldFailKnownFailureIssueValidation,
9
- validateKnownFailureIssues,
10
- } from "../known-failures/github.mjs";
11
- import {
12
- loadKnownFailuresDocument,
13
- renderKnownFailuresMarkdown,
14
- validateKnownFailuresDocument,
15
- } from "../known-failures/index.mjs";
16
- import { collectGitMetadata } from "../runner/metadata.mjs";
17
-
18
- const DEFAULT_ISSUE_VALIDATION = {
19
- provider: "github",
20
- mode: "error",
21
- cacheTtlSeconds: 900,
22
- };
23
-
24
- export async function runKnownFailuresRenderCommand(options = {}) {
25
- const context = await resolveKnownFailuresContext(options);
26
- const document = loadKnownFailuresDocument(context.knownFailuresPath, context.knownFailuresRelativePath);
27
- const markdown = renderKnownFailuresMarkdown(document);
28
-
29
- if (options.output) {
30
- const outputPath = path.resolve(context.productDir, options.output);
31
- fs.mkdirSync(path.dirname(outputPath), { recursive: true });
32
- fs.writeFileSync(outputPath, markdown);
33
- console.log(`Wrote ${path.relative(context.productDir, outputPath)}`);
34
- return;
35
- }
36
-
37
- process.stdout.write(markdown);
38
- }
39
-
40
- export async function runKnownFailuresValidateCommand(options = {}) {
41
- const context = await resolveKnownFailuresContext(options);
42
- const document = loadKnownFailuresDocument(context.knownFailuresPath, context.knownFailuresRelativePath);
43
- const statusArtifact = context.statusArtifactPath
44
- ? readJsonIfExists(context.statusArtifactPath)
45
- : undefined;
46
-
47
- const documentValidation = validateKnownFailuresDocument(document, {
48
- productDir: context.productDir,
49
- statusArtifact,
50
- });
51
-
52
- const issueValidation = await validateKnownFailureIssues({
53
- productDir: context.productDir,
54
- document,
55
- statusArtifact,
56
- config: context.issueValidationConfig,
57
- gitMetadata: collectGitMetadata(context.productDir),
58
- });
59
-
60
- const issueErrors = collectIssueMessages(issueValidation, "error");
61
- const issueWarnings = collectIssueMessages(issueValidation, "warning");
62
- const hasErrors =
63
- documentValidation.errors.length > 0 ||
64
- shouldFailKnownFailureIssueValidation(issueValidation) ||
65
- issueErrors.length > 0;
66
-
67
- if (hasErrors) {
68
- console.error("Known failures validation failed:");
69
- for (const error of documentValidation.errors) {
70
- console.error(`- ${error}`);
71
- }
72
- for (const error of issueErrors) {
73
- console.error(`- ${error}`);
74
- }
75
- if (documentValidation.warnings.length > 0 || issueWarnings.length > 0) {
76
- console.error("");
77
- for (const warning of documentValidation.warnings) {
78
- console.error(`! ${warning}`);
79
- }
80
- for (const warning of issueWarnings) {
81
- console.error(`! ${warning}`);
82
- }
83
- }
84
- for (const line of buildKnownFailureIssueValidationSummaryLines(issueValidation)) {
85
- console.error(line);
86
- }
87
- process.exitCode = 1;
88
- return;
89
- }
90
-
91
- console.log(
92
- `Known failures valid: ${documentValidation.stats.entries} entries, ${documentValidation.stats.matches} matches`
93
- );
94
- if (documentValidation.stats.failedTests > 0) {
95
- console.log(
96
- `Status coverage: ${documentValidation.stats.triagedFailedTests}/${documentValidation.stats.failedTests} failed tests triaged`
97
- );
98
- }
99
- for (const line of buildKnownFailureIssueValidationSummaryLines(issueValidation)) {
100
- console.log(line);
101
- }
102
- for (const warning of [...documentValidation.warnings, ...issueWarnings]) {
103
- console.warn(`! ${warning}`);
104
- }
105
- }
106
-
107
- async function resolveKnownFailuresContext(options) {
108
- const productDir = resolveProductDir(process.cwd(), options.dir);
109
- const { config } = await loadTestkitConfig(productDir);
110
- const reporting = config?.reporting || {};
111
-
112
- const knownFailuresRelativePath = normalizeOptionalString(options.input)
113
- || normalizeOptionalString(reporting.knownFailuresFile);
114
- if (!knownFailuresRelativePath) {
115
- throw new Error(
116
- "Known failures file not configured. Set reporting.knownFailuresFile in testkit.config.ts or pass --input."
117
- );
118
- }
119
-
120
- const statusRelativePath = normalizeOptionalString(options.status) || "testkit.status.json";
121
- const configuredIssueValidation = normalizeKnownFailureIssueValidationConfig(reporting.issueValidation)
122
- || DEFAULT_ISSUE_VALIDATION;
123
- const issueMode = normalizeOptionalString(options.issueMode) || "error";
124
-
125
- return {
126
- productDir,
127
- knownFailuresRelativePath,
128
- knownFailuresPath: path.resolve(productDir, knownFailuresRelativePath),
129
- statusArtifactPath: statusRelativePath ? path.resolve(productDir, statusRelativePath) : null,
130
- issueValidationConfig: {
131
- ...configuredIssueValidation,
132
- mode: issueMode,
133
- },
134
- };
135
- }
136
-
137
- function collectIssueMessages(validation, severity) {
138
- if (!validation) return [];
139
-
140
- const messages = [
141
- ...validation.findings
142
- .filter((finding) => finding.severity === severity)
143
- .map((finding) => finding.message),
144
- ];
145
- for (const entry of validation.entries) {
146
- for (const finding of entry.findings) {
147
- if (finding.severity === severity) {
148
- messages.push(finding.message);
149
- }
150
- }
151
- }
152
- return [...new Set(messages)];
153
- }
154
-
155
- function normalizeOptionalString(value) {
156
- if (typeof value !== "string") return null;
157
- const normalized = value.trim();
158
- return normalized.length > 0 ? normalized : null;
159
- }
160
-
161
- function readJsonIfExists(filePath) {
162
- if (!fs.existsSync(filePath)) return undefined;
163
- return JSON.parse(fs.readFileSync(filePath, "utf8"));
164
- }