@elench/testkit 0.1.60 → 0.1.62

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 (36) hide show
  1. package/lib/config/database.mjs +53 -0
  2. package/lib/config/database.test.mjs +29 -0
  3. package/lib/config/discovery-config.mjs +13 -0
  4. package/lib/config/env.mjs +55 -0
  5. package/lib/config/env.test.mjs +40 -0
  6. package/lib/config/index.mjs +21 -807
  7. package/lib/config/paths.mjs +28 -0
  8. package/lib/config/paths.test.mjs +27 -0
  9. package/lib/config/runtime.mjs +241 -0
  10. package/lib/config/runtime.test.mjs +56 -0
  11. package/lib/config/skip-config.mjs +189 -0
  12. package/lib/config/skip-config.test.mjs +63 -0
  13. package/lib/config/telemetry.mjs +28 -0
  14. package/lib/config/validation.mjs +124 -0
  15. package/lib/coverage/backend-discovery.mjs +183 -0
  16. package/lib/coverage/backend-discovery.test.mjs +52 -0
  17. package/lib/coverage/evidence.mjs +147 -0
  18. package/lib/coverage/evidence.test.mjs +77 -0
  19. package/lib/coverage/fs-walk.mjs +64 -0
  20. package/lib/coverage/graph-builder.mjs +181 -0
  21. package/lib/coverage/index.mjs +1 -816
  22. package/lib/coverage/index.test.mjs +330 -14
  23. package/lib/coverage/next-discovery.mjs +174 -0
  24. package/lib/coverage/next-static-analysis.mjs +763 -0
  25. package/lib/coverage/routing.mjs +86 -0
  26. package/lib/coverage/routing.test.mjs +52 -0
  27. package/lib/coverage/shared.mjs +198 -0
  28. package/lib/coverage/shared.test.mjs +39 -0
  29. package/node_modules/@elench/testkit-bridge/package.json +2 -2
  30. package/node_modules/@elench/testkit-bridge/src/index.mjs +156 -13
  31. package/node_modules/@elench/testkit-bridge/src/index.test.mjs +39 -5
  32. package/node_modules/@elench/testkit-protocol/package.json +1 -1
  33. package/node_modules/@elench/testkit-protocol/src/index.d.ts +26 -0
  34. package/node_modules/@elench/testkit-protocol/src/index.mjs +18 -0
  35. package/node_modules/@elench/testkit-protocol/src/index.test.mjs +75 -1
  36. package/package.json +5 -4
@@ -45,6 +45,19 @@ const context = {
45
45
  route: "/coverage",
46
46
  filePath: "app/coverage/page.tsx",
47
47
  },
48
+ {
49
+ id: "ui_surface:web:/coverage:coverage-refresh-button",
50
+ kind: "ui_surface",
51
+ service: "web",
52
+ label: "Refresh overlay",
53
+ route: "/coverage",
54
+ filePath: "app/coverage/page.tsx",
55
+ target: {
56
+ kind: "testId",
57
+ value: "coverage-refresh-button",
58
+ confidence: "high",
59
+ },
60
+ },
48
61
  {
49
62
  id: "api_route:web:GET:/api/coverage",
50
63
  kind: "api_route",
@@ -72,9 +85,16 @@ const context = {
72
85
  ],
73
86
  edges: [
74
87
  {
75
- id: "requests:page_view:web:/coverage:api_route:web:GET:/api/coverage",
76
- kind: "requests",
88
+ id: "contains:page_view:web:/coverage:ui_surface:web:/coverage:coverage-refresh-button",
89
+ kind: "contains",
77
90
  from: "page_view:web:/coverage",
91
+ to: "ui_surface:web:/coverage:coverage-refresh-button",
92
+ confidence: "high",
93
+ },
94
+ {
95
+ id: "requests:ui_surface:web:/coverage:coverage-refresh-button:api_route:web:GET:/api/coverage",
96
+ kind: "requests",
97
+ from: "ui_surface:web:/coverage:coverage-refresh-button",
78
98
  to: "api_route:web:GET:/api/coverage",
79
99
  confidence: "high",
80
100
  },
@@ -89,7 +109,10 @@ const context = {
89
109
  selectionType: "pw",
90
110
  framework: "playwright",
91
111
  testFilePath: "app/coverage/__testkit__/coverage.pw.testkit.ts",
92
- coveredNodeIds: ["page_view:web:/coverage"],
112
+ coveredNodeIds: [
113
+ "page_view:web:/coverage",
114
+ "ui_surface:web:/coverage:coverage-refresh-button",
115
+ ],
93
116
  details: {
94
117
  route: "/coverage",
95
118
  targets: [{ kind: "testId", value: "coverage-refresh-button", confidence: "high" }],
@@ -156,10 +179,18 @@ describe("testkit bridge", () => {
156
179
  coverageState: "covered",
157
180
  relatedFailureCount: 1,
158
181
  relatedCoverageCount: 1,
182
+ coverageBreakdown: {
183
+ direct: 0,
184
+ indirect: 0,
185
+ mixed: 1,
186
+ },
159
187
  },
160
188
  failures: [
161
189
  {
162
- label: "Coverage",
190
+ kind: "ui_surface",
191
+ label: "Refresh overlay",
192
+ importance: "high",
193
+ reason: "Refresh overlay is implicated by 1 failing test.",
163
194
  targets: [{ kind: "testId", value: "coverage-refresh-button", confidence: "high" }],
164
195
  failedTests: [
165
196
  {
@@ -171,7 +202,10 @@ describe("testkit bridge", () => {
171
202
  ],
172
203
  coverage: [
173
204
  {
174
- label: "Coverage",
205
+ kind: "ui_surface",
206
+ label: "Refresh overlay",
207
+ importance: "high",
208
+ supportKind: "mixed",
175
209
  targets: [{ kind: "testId", value: "coverage-refresh-button", confidence: "high" }],
176
210
  supportingTests: [
177
211
  {
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@elench/testkit-protocol",
3
- "version": "0.1.60",
3
+ "version": "0.1.62",
4
4
  "description": "Shared browser protocol for testkit bridge and extension consumers",
5
5
  "type": "module",
6
6
  "main": "./src/index.mjs",
@@ -5,6 +5,8 @@ export type BrowserTargetKind = "testId" | "role" | "text" | "css" | "xpath" | "
5
5
  export type BrowserConfidence = "low" | "medium" | "high";
6
6
  export type BrowserFailureState = "failing" | "healthy" | "unavailable";
7
7
  export type BrowserCoverageState = "covered" | "missing" | "unavailable";
8
+ export type BridgeSurfaceImportance = "critical" | "high" | "medium" | "low";
9
+ export type BridgeCoverageSupportKind = "direct" | "indirect" | "mixed";
8
10
 
9
11
  export type CoverageNodeKind =
10
12
  | "page_view"
@@ -86,11 +88,22 @@ export interface CoverageEvidence {
86
88
  };
87
89
  }
88
90
 
91
+ export type CoverageGraphDiagnosticLevel = "info" | "warn";
92
+
93
+ export interface CoverageGraphDiagnostic {
94
+ level: CoverageGraphDiagnosticLevel;
95
+ code: string;
96
+ filePath: string;
97
+ service: string;
98
+ message: string;
99
+ }
100
+
89
101
  export interface CoverageGraph {
90
102
  schemaVersion: number;
91
103
  nodes: CoverageGraphNode[];
92
104
  edges: CoverageGraphEdge[];
93
105
  evidence: CoverageEvidence[];
106
+ diagnostics: CoverageGraphDiagnostic[];
94
107
  }
95
108
 
96
109
  export interface BridgeProductRef {
@@ -149,6 +162,9 @@ export interface FailureOverlayEntry {
149
162
  targets: BrowserTarget[];
150
163
  failedTests: BridgeSupportingTestRef[];
151
164
  viaNodes: BridgeCoverageViaNodeRef[];
165
+ importance?: BridgeSurfaceImportance;
166
+ surfaceKind?: string | null;
167
+ reason?: string | null;
152
168
  }
153
169
 
154
170
  export interface CoverageOverlayEntry {
@@ -161,6 +177,10 @@ export interface CoverageOverlayEntry {
161
177
  supportingTests: BridgeSupportingTestRef[];
162
178
  viaNodes: BridgeCoverageViaNodeRef[];
163
179
  confidence: BrowserConfidence;
180
+ importance?: BridgeSurfaceImportance;
181
+ surfaceKind?: string | null;
182
+ supportKind?: BridgeCoverageSupportKind;
183
+ reason?: string | null;
164
184
  }
165
185
 
166
186
  export interface PageOverlayResponse {
@@ -177,6 +197,11 @@ export interface PageOverlayResponse {
177
197
  coverageState: BrowserCoverageState;
178
198
  relatedFailureCount: number;
179
199
  relatedCoverageCount: number;
200
+ coverageBreakdown?: {
201
+ direct: number;
202
+ indirect: number;
203
+ mixed: number;
204
+ };
180
205
  };
181
206
  failures: FailureOverlayEntry[];
182
207
  coverage: CoverageOverlayEntry[];
@@ -196,6 +221,7 @@ export declare function normalizeBrowserMetadataDocument(value: unknown): Browse
196
221
  export declare function normalizeCoverageGraphNode(value: unknown): CoverageGraphNode | null;
197
222
  export declare function normalizeCoverageGraphEdge(value: unknown): CoverageGraphEdge | null;
198
223
  export declare function normalizeCoverageEvidence(value: unknown): CoverageEvidence | null;
224
+ export declare function normalizeCoverageGraphDiagnostic(value: unknown): CoverageGraphDiagnostic | null;
199
225
  export declare function normalizeCoverageGraph(value: unknown): CoverageGraph | null;
200
226
  export declare function normalizePageOverlayResponse(value: unknown): PageOverlayResponse | null;
201
227
  export declare function isPageOverlayResponse(value: unknown): value is PageOverlayResponse;
@@ -158,6 +158,20 @@ export function normalizeCoverageEvidence(value) {
158
158
  };
159
159
  }
160
160
 
161
+ const DIAGNOSTIC_LEVELS = new Set(["info", "warn"]);
162
+
163
+ export function normalizeCoverageGraphDiagnostic(value) {
164
+ if (!value || typeof value !== "object" || Array.isArray(value)) return null;
165
+ const level = normalizeOptionalString(value.level);
166
+ const code = normalizeOptionalString(value.code);
167
+ const filePath = normalizeOptionalString(value.filePath);
168
+ const service = normalizeOptionalString(value.service);
169
+ const message = normalizeOptionalString(value.message);
170
+ if (!level || !code || !filePath || !service || !message) return null;
171
+ if (!DIAGNOSTIC_LEVELS.has(level)) return null;
172
+ return { level, code, filePath, service, message };
173
+ }
174
+
161
175
  export function normalizeCoverageGraph(value) {
162
176
  if (!value || typeof value !== "object" || Array.isArray(value)) return null;
163
177
  const schemaVersion =
@@ -169,12 +183,16 @@ export function normalizeCoverageGraph(value) {
169
183
  const evidence = Array.isArray(value.evidence)
170
184
  ? value.evidence.map(normalizeCoverageEvidence).filter(Boolean)
171
185
  : [];
186
+ const diagnostics = Array.isArray(value.diagnostics)
187
+ ? value.diagnostics.map(normalizeCoverageGraphDiagnostic).filter(Boolean)
188
+ : [];
172
189
  if (nodes.length === 0) return null;
173
190
  return {
174
191
  schemaVersion,
175
192
  nodes,
176
193
  edges,
177
194
  evidence,
195
+ diagnostics,
178
196
  };
179
197
  }
180
198
 
@@ -6,6 +6,7 @@ import {
6
6
  normalizeBrowserTarget,
7
7
  normalizeCoverageEvidence,
8
8
  normalizeCoverageGraph,
9
+ normalizeCoverageGraphDiagnostic,
9
10
  normalizeCoverageGraphEdge,
10
11
  normalizeCoverageGraphNode,
11
12
  } from "./index.mjs";
@@ -107,7 +108,79 @@ describe("testkit browser protocol", () => {
107
108
  });
108
109
  });
109
110
 
110
- it("normalizes graph payloads", () => {
111
+ it("normalizes coverage graph diagnostics", () => {
112
+ expect(
113
+ normalizeCoverageGraphDiagnostic({
114
+ level: "warn",
115
+ code: "no-service-context",
116
+ filePath: "tests/example.int.testkit.ts",
117
+ service: "web",
118
+ message: 'No coverage context available for service "web".',
119
+ })
120
+ ).toEqual({
121
+ level: "warn",
122
+ code: "no-service-context",
123
+ filePath: "tests/example.int.testkit.ts",
124
+ service: "web",
125
+ message: 'No coverage context available for service "web".',
126
+ });
127
+
128
+ expect(normalizeCoverageGraphDiagnostic({ level: "error", code: "x", filePath: "f", service: "s", message: "m" })).toBeNull();
129
+ expect(normalizeCoverageGraphDiagnostic({ level: "warn", code: "", filePath: "f", service: "s", message: "m" })).toBeNull();
130
+ expect(normalizeCoverageGraphDiagnostic(null)).toBeNull();
131
+ });
132
+
133
+ it("normalizes graph payloads with diagnostics", () => {
134
+ expect(
135
+ normalizeCoverageGraph({
136
+ schemaVersion: TESTKIT_COVERAGE_GRAPH_VERSION,
137
+ nodes: [
138
+ {
139
+ id: "page_view:web:/coverage",
140
+ kind: "page_view",
141
+ service: "web",
142
+ label: "Coverage",
143
+ route: "/coverage",
144
+ },
145
+ ],
146
+ edges: [],
147
+ evidence: [],
148
+ diagnostics: [
149
+ {
150
+ level: "info",
151
+ code: "zero-coverage-inferred",
152
+ filePath: "tests/example.pw.testkit.ts",
153
+ service: "web",
154
+ message: "No routes matched.",
155
+ },
156
+ ],
157
+ })
158
+ ).toEqual({
159
+ schemaVersion: TESTKIT_COVERAGE_GRAPH_VERSION,
160
+ nodes: [
161
+ {
162
+ id: "page_view:web:/coverage",
163
+ kind: "page_view",
164
+ service: "web",
165
+ label: "Coverage",
166
+ route: "/coverage",
167
+ },
168
+ ],
169
+ edges: [],
170
+ evidence: [],
171
+ diagnostics: [
172
+ {
173
+ level: "info",
174
+ code: "zero-coverage-inferred",
175
+ filePath: "tests/example.pw.testkit.ts",
176
+ service: "web",
177
+ message: "No routes matched.",
178
+ },
179
+ ],
180
+ });
181
+ });
182
+
183
+ it("normalizes graph payloads without diagnostics field", () => {
111
184
  expect(
112
185
  normalizeCoverageGraph({
113
186
  schemaVersion: TESTKIT_COVERAGE_GRAPH_VERSION,
@@ -136,6 +209,7 @@ describe("testkit browser protocol", () => {
136
209
  ],
137
210
  edges: [],
138
211
  evidence: [],
212
+ diagnostics: [],
139
213
  });
140
214
  });
141
215
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@elench/testkit",
3
- "version": "0.1.60",
3
+ "version": "0.1.62",
4
4
  "description": "CLI for discovering and running local HTTP, DAL, and Playwright test suites",
5
5
  "type": "module",
6
6
  "workspaces": [
@@ -59,15 +59,16 @@
59
59
  "vitest": "^3.2.4"
60
60
  },
61
61
  "dependencies": {
62
- "@elench/testkit-bridge": "0.1.60",
63
- "@elench/testkit-protocol": "0.1.60",
62
+ "@elench/testkit-bridge": "0.1.62",
63
+ "@elench/testkit-protocol": "0.1.62",
64
64
  "@babel/code-frame": "^7.29.0",
65
65
  "@oclif/core": "^4.10.6",
66
66
  "esbuild": "^0.25.11",
67
67
  "execa": "^9.5.0",
68
68
  "ink": "^7.0.1",
69
69
  "picocolors": "^1.1.1",
70
- "react": "^19.2.5"
70
+ "react": "^19.2.5",
71
+ "typescript": "^5.9.3"
71
72
  },
72
73
  "engines": {
73
74
  "node": ">=18"