@pretorian-worx/runclaudia-core 0.12.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.
- package/LICENSE +21 -0
- package/README.md +16 -0
- package/dist/adapters/nextjs.d.ts +27 -0
- package/dist/adapters/nextjs.js +457 -0
- package/dist/adapters/nextjs.js.map +1 -0
- package/dist/adapters/prisma.d.ts +22 -0
- package/dist/adapters/prisma.js +80 -0
- package/dist/adapters/prisma.js.map +1 -0
- package/dist/adapters/specs.d.ts +13 -0
- package/dist/adapters/specs.js +126 -0
- package/dist/adapters/specs.js.map +1 -0
- package/dist/adapters/terraform.d.ts +17 -0
- package/dist/adapters/terraform.js +82 -0
- package/dist/adapters/terraform.js.map +1 -0
- package/dist/diff.d.ts +13 -0
- package/dist/diff.js +187 -0
- package/dist/diff.js.map +1 -0
- package/dist/gating.d.ts +29 -0
- package/dist/gating.js +59 -0
- package/dist/gating.js.map +1 -0
- package/dist/index.d.ts +15 -0
- package/dist/index.js +12 -0
- package/dist/index.js.map +1 -0
- package/dist/llm.d.ts +24 -0
- package/dist/llm.js +135 -0
- package/dist/llm.js.map +1 -0
- package/dist/map.d.ts +9 -0
- package/dist/map.js +55 -0
- package/dist/map.js.map +1 -0
- package/dist/plan-format.d.ts +3 -0
- package/dist/plan-format.js +71 -0
- package/dist/plan-format.js.map +1 -0
- package/dist/plan.d.ts +21 -0
- package/dist/plan.js +44 -0
- package/dist/plan.js.map +1 -0
- package/dist/prompt.d.ts +40 -0
- package/dist/prompt.js +266 -0
- package/dist/prompt.js.map +1 -0
- package/dist/runner.d.ts +53 -0
- package/dist/runner.js +119 -0
- package/dist/runner.js.map +1 -0
- package/dist/select.d.ts +31 -0
- package/dist/select.js +108 -0
- package/dist/select.js.map +1 -0
- package/dist/types.d.ts +176 -0
- package/dist/types.js +17 -0
- package/dist/types.js.map +1 -0
- package/package.json +54 -0
package/dist/prompt.js
ADDED
|
@@ -0,0 +1,266 @@
|
|
|
1
|
+
export const SYSTEM_PROMPT = `You are claudia, a diff-aware test planner.
|
|
2
|
+
|
|
3
|
+
Your job: read a code diff plus a map of the app's routes and API endpoints, then output a structured plan describing which user-facing flows a human tester should exercise to validate the change. You DO NOT execute tests. You produce a plan a reviewer or downstream tool will act on.
|
|
4
|
+
|
|
5
|
+
Rules:
|
|
6
|
+
- Be specific. Cite changed file paths in your reasoning.
|
|
7
|
+
- Map every changed file to the routes AND endpoints it reaches. If a file is not in the map, list it under unmappedFiles and explain why it might still matter.
|
|
8
|
+
- Distinguish API changes from UI changes:
|
|
9
|
+
- A changed page/component implies a flow on its route(s) — write checks as user actions.
|
|
10
|
+
- A changed endpoint (route.ts) implies an API contract change — call out the method + path, the request body shape (json/formData/text/etc), and recommend exercising it via the UI flow that hits it OR directly (curl/API client) when no UI flow is implicated.
|
|
11
|
+
- A changed component that *calls* an endpoint (statically detected — see "Endpoints called by changed files" below) implies a full-stack flow: the UI change AND the contract between UI and that endpoint. Verify the end-to-end roundtrip, not just the rendered output.
|
|
12
|
+
- The flow.routes field can contain either page paths ("/checkout") or method-prefixed endpoint paths ("POST /api/bugs/move"). Use whichever fits the change.
|
|
13
|
+
- Risk levels: "high" = auth, payments, data-mutation, schema changes, or many routes/endpoints affected; "medium" = single-route behavior change or additive endpoint; "low" = cosmetic, copy, isolated UI.
|
|
14
|
+
- Suggested checks must be concrete user actions ("complete checkout with a saved card", not "test the checkout flow") or concrete API checks ("POST /api/bugs/move with a valid payload; expect 200 + new bug ref").
|
|
15
|
+
- Set verdict to "skip" only if the diff genuinely cannot affect runtime behavior (already-filtered cases shouldn't reach you, so prefer "test").
|
|
16
|
+
- coverageGaps captures *unmapped risk* — changes you can see have impact but no flow or endpoint in the map covers them.
|
|
17
|
+
- Treat infrastructure changes as production-impact risk. If the diff includes Terraform/CDK resources and any endpoint in the diff touches the same service (per the endpoint's "services" annotation), call out the coordinated risk — e.g. "S3 bucket policy changed AND POST /api/attachments writes to S3, verify the write still succeeds end-to-end."
|
|
18
|
+
- Treat database-schema changes as high-risk by default. Endpoints carry a "tables" annotation listing the DB models they touch (e.g. a Prisma call like prisma.bug.create(...) maps to ["Bug"]). When the diff changes the schema for a model AND an endpoint in the diff (or called by the diff) touches that model, call out the read/write contract explicitly — "the Bug model gained a non-null column; POST /api/bugs writes to Bug, verify the new column is populated."
|
|
19
|
+
- When the prompt's "Existing test coverage" section lists specs that already cover the affected routes/endpoints, reference them by file:name in your suggestedChecks — e.g. "Run e2e/checkout.spec.ts:'completes checkout' against the deploy." Recommending existing specs is cheaper for the team than writing new ones and is preferred when coverage exists.
|
|
20
|
+
- coverageGaps should call out flows the diff implicates that have NO existing spec — that's a concrete signal to the team to add one.
|
|
21
|
+
- Be terse. The output is read by humans on a PR.`;
|
|
22
|
+
export function filterMapForDiff(map, diff) {
|
|
23
|
+
const diffPaths = new Set();
|
|
24
|
+
for (const f of diff.files) {
|
|
25
|
+
diffPaths.add(f.path);
|
|
26
|
+
if (f.oldPath)
|
|
27
|
+
diffPaths.add(f.oldPath);
|
|
28
|
+
}
|
|
29
|
+
const implicated = map.routes.filter((r) => r.files.some((file) => diffPaths.has(file)));
|
|
30
|
+
const endpoints = map.endpoints ?? [];
|
|
31
|
+
const implicatedEndpoints = endpoints.filter((e) => diffPaths.has(e.file));
|
|
32
|
+
// Indirect linkage: a changed file calls an endpoint whose handler isn't itself
|
|
33
|
+
// in the diff. We want the brain to consider the contract between the UI and
|
|
34
|
+
// that endpoint as part of the flow.
|
|
35
|
+
const fileToEndpoints = map.fileToEndpoints ?? {};
|
|
36
|
+
const directlyChangedRoutes = new Set(implicatedEndpoints.map((e) => e.route));
|
|
37
|
+
const calledRouteToCallers = new Map();
|
|
38
|
+
for (const file of diffPaths) {
|
|
39
|
+
const calls = fileToEndpoints[file] ?? [];
|
|
40
|
+
for (const route of calls) {
|
|
41
|
+
if (directlyChangedRoutes.has(route))
|
|
42
|
+
continue;
|
|
43
|
+
const list = calledRouteToCallers.get(route) ?? [];
|
|
44
|
+
list.push(file);
|
|
45
|
+
calledRouteToCallers.set(route, list);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
const endpointsByRoute = new Map(endpoints.map((e) => [e.route, e]));
|
|
49
|
+
const endpointsCalledByDiff = [];
|
|
50
|
+
for (const [route, callerFiles] of calledRouteToCallers) {
|
|
51
|
+
const endpoint = endpointsByRoute.get(route);
|
|
52
|
+
if (!endpoint)
|
|
53
|
+
continue;
|
|
54
|
+
endpointsCalledByDiff.push({
|
|
55
|
+
endpoint,
|
|
56
|
+
callerFiles: Array.from(new Set(callerFiles)).sort(),
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
endpointsCalledByDiff.sort((a, b) => a.endpoint.route.localeCompare(b.endpoint.route));
|
|
60
|
+
const infra = map.infra ?? [];
|
|
61
|
+
const implicatedInfra = infra.filter((r) => diffPaths.has(r.file));
|
|
62
|
+
const dbModels = map.dbModels ?? [];
|
|
63
|
+
const implicatedDbModels = dbModels.filter((m) => diffPaths.has(m.file));
|
|
64
|
+
// Spec coverage: a spec "covers" the diff if any of its tracked routes or
|
|
65
|
+
// endpoints intersects with the implicated set (direct OR called-by-diff).
|
|
66
|
+
const allImplicatedRoutes = new Set([
|
|
67
|
+
...implicated.map((r) => r.route),
|
|
68
|
+
]);
|
|
69
|
+
const allImplicatedEndpointRoutes = new Set([
|
|
70
|
+
...implicatedEndpoints.map((e) => e.route),
|
|
71
|
+
...endpointsCalledByDiff.map((s) => s.endpoint.route),
|
|
72
|
+
]);
|
|
73
|
+
const specs = map.specs ?? [];
|
|
74
|
+
const coveringSpecs = specs.filter((s) => s.routesCovered.some((r) => allImplicatedRoutes.has(r)) ||
|
|
75
|
+
s.endpointsCovered.some((e) => allImplicatedEndpointRoutes.has(e)));
|
|
76
|
+
const coveredRoutes = new Set(coveringSpecs.flatMap((s) => s.routesCovered));
|
|
77
|
+
const coveredEndpoints = new Set(coveringSpecs.flatMap((s) => s.endpointsCovered));
|
|
78
|
+
const uncoveredRoutes = Array.from(allImplicatedRoutes).filter((r) => !coveredRoutes.has(r)).sort();
|
|
79
|
+
const uncoveredEndpoints = Array.from(allImplicatedEndpointRoutes).filter((e) => !coveredEndpoints.has(e)).sort();
|
|
80
|
+
return {
|
|
81
|
+
implicated,
|
|
82
|
+
omittedCount: map.routes.length - implicated.length,
|
|
83
|
+
implicatedEndpoints,
|
|
84
|
+
omittedEndpointCount: endpoints.length - implicatedEndpoints.length,
|
|
85
|
+
endpointsCalledByDiff,
|
|
86
|
+
implicatedInfra,
|
|
87
|
+
omittedInfraCount: infra.length - implicatedInfra.length,
|
|
88
|
+
implicatedDbModels,
|
|
89
|
+
omittedDbModelCount: dbModels.length - implicatedDbModels.length,
|
|
90
|
+
coveringSpecs,
|
|
91
|
+
uncoveredRoutes,
|
|
92
|
+
uncoveredEndpoints,
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
export function buildUserMessage(args) {
|
|
96
|
+
const { diff, map, targetUrl } = args;
|
|
97
|
+
const { implicated, omittedCount, implicatedEndpoints, omittedEndpointCount, endpointsCalledByDiff, implicatedInfra, omittedInfraCount, implicatedDbModels, omittedDbModelCount, coveringSpecs, uncoveredRoutes, uncoveredEndpoints, } = filterMapForDiff(map, diff);
|
|
98
|
+
const totalInfra = (map.infra ?? []).length;
|
|
99
|
+
const totalDbModels = (map.dbModels ?? []).length;
|
|
100
|
+
const totalSpecs = (map.specs ?? []).length;
|
|
101
|
+
const totalEndpoints = (map.endpoints ?? []).length;
|
|
102
|
+
const parts = [];
|
|
103
|
+
parts.push(`# Route map (framework: ${map.framework})`);
|
|
104
|
+
if (map.routes.length === 0) {
|
|
105
|
+
parts.push("(no routes discovered)");
|
|
106
|
+
}
|
|
107
|
+
else if (implicated.length === 0) {
|
|
108
|
+
parts.push(`(none of the ${map.routes.length} known routes are touched by this diff)`);
|
|
109
|
+
}
|
|
110
|
+
else {
|
|
111
|
+
parts.push(`Showing ${implicated.length} of ${map.routes.length} known routes — only those whose tracked files appear in the diff.`);
|
|
112
|
+
parts.push("");
|
|
113
|
+
for (const r of implicated) {
|
|
114
|
+
parts.push(`- ${r.route}`);
|
|
115
|
+
for (const f of r.files)
|
|
116
|
+
parts.push(` - ${f}`);
|
|
117
|
+
}
|
|
118
|
+
if (omittedCount > 0) {
|
|
119
|
+
parts.push("");
|
|
120
|
+
parts.push(`(${omittedCount} other routes exist in this project but are not affected by this diff.)`);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
parts.push("");
|
|
124
|
+
parts.push(`# API endpoints`);
|
|
125
|
+
if (totalEndpoints === 0) {
|
|
126
|
+
parts.push("(no endpoints discovered)");
|
|
127
|
+
}
|
|
128
|
+
else if (implicatedEndpoints.length === 0) {
|
|
129
|
+
parts.push(`(none of the ${totalEndpoints} known endpoints are touched by this diff)`);
|
|
130
|
+
}
|
|
131
|
+
else {
|
|
132
|
+
parts.push(`Showing ${implicatedEndpoints.length} of ${totalEndpoints} known endpoints — only those whose handler file appears in the diff.`);
|
|
133
|
+
parts.push("");
|
|
134
|
+
for (const e of implicatedEndpoints) {
|
|
135
|
+
parts.push(`- ${e.method} ${e.path}${endpointAnnotations(e)}`);
|
|
136
|
+
parts.push(` - ${e.file}`);
|
|
137
|
+
}
|
|
138
|
+
if (omittedEndpointCount > 0) {
|
|
139
|
+
parts.push("");
|
|
140
|
+
parts.push(`(${omittedEndpointCount} other endpoints exist in this project but are not affected by this diff.)`);
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
parts.push("");
|
|
144
|
+
parts.push(`# Endpoints called by changed files`);
|
|
145
|
+
if (endpointsCalledByDiff.length === 0) {
|
|
146
|
+
parts.push("(no static call sites detected from files in this diff)");
|
|
147
|
+
}
|
|
148
|
+
else {
|
|
149
|
+
parts.push(`Statically detected call sites — when these files change, the contract with the endpoint may be affected.`);
|
|
150
|
+
parts.push("");
|
|
151
|
+
for (const site of endpointsCalledByDiff) {
|
|
152
|
+
parts.push(`- ${site.endpoint.method} ${site.endpoint.path}${endpointAnnotations(site.endpoint)}`);
|
|
153
|
+
parts.push(` - handler: ${site.endpoint.file}`);
|
|
154
|
+
for (const c of site.callerFiles)
|
|
155
|
+
parts.push(` - called by: ${c}`);
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
parts.push("");
|
|
159
|
+
parts.push(`# Infrastructure (Terraform)`);
|
|
160
|
+
if (totalInfra === 0) {
|
|
161
|
+
parts.push("(no infrastructure resources discovered)");
|
|
162
|
+
}
|
|
163
|
+
else if (implicatedInfra.length === 0) {
|
|
164
|
+
parts.push(`(none of the ${totalInfra} known resources are touched by this diff)`);
|
|
165
|
+
}
|
|
166
|
+
else {
|
|
167
|
+
parts.push(`Showing ${implicatedInfra.length} of ${totalInfra} known resources — only those whose declaration file is in the diff.`);
|
|
168
|
+
parts.push("");
|
|
169
|
+
for (const r of implicatedInfra) {
|
|
170
|
+
parts.push(`- ${r.address} (${r.tool})`);
|
|
171
|
+
parts.push(` - ${r.file}`);
|
|
172
|
+
}
|
|
173
|
+
if (omittedInfraCount > 0) {
|
|
174
|
+
parts.push("");
|
|
175
|
+
parts.push(`(${omittedInfraCount} other resources exist in this project but are not affected by this diff.)`);
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
parts.push("");
|
|
179
|
+
parts.push(`# Database schema (Prisma)`);
|
|
180
|
+
if (totalDbModels === 0) {
|
|
181
|
+
parts.push("(no models discovered)");
|
|
182
|
+
}
|
|
183
|
+
else if (implicatedDbModels.length === 0) {
|
|
184
|
+
parts.push(`(none of the ${totalDbModels} known models are touched by this diff)`);
|
|
185
|
+
}
|
|
186
|
+
else {
|
|
187
|
+
parts.push(`Showing ${implicatedDbModels.length} of ${totalDbModels} known models — only those whose schema file is in the diff.`);
|
|
188
|
+
parts.push("");
|
|
189
|
+
for (const m of implicatedDbModels) {
|
|
190
|
+
parts.push(`- ${m.name} (${m.orm})`);
|
|
191
|
+
parts.push(` - ${m.file}`);
|
|
192
|
+
}
|
|
193
|
+
if (omittedDbModelCount > 0) {
|
|
194
|
+
parts.push("");
|
|
195
|
+
parts.push(`(${omittedDbModelCount} other models exist in this project but are not affected by this diff.)`);
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
parts.push("");
|
|
199
|
+
parts.push(`# Existing test coverage (Playwright / Cypress)`);
|
|
200
|
+
if (totalSpecs === 0) {
|
|
201
|
+
parts.push("(no specs discovered — no e2e/, playwright/, or cypress/ directory found)");
|
|
202
|
+
}
|
|
203
|
+
else {
|
|
204
|
+
if (coveringSpecs.length === 0) {
|
|
205
|
+
parts.push(`(${totalSpecs} specs indexed; none cover the routes/endpoints touched by this diff)`);
|
|
206
|
+
}
|
|
207
|
+
else {
|
|
208
|
+
parts.push(`Specs that cover affected flows — prefer recommending these over writing new tests.`);
|
|
209
|
+
parts.push("");
|
|
210
|
+
for (const s of coveringSpecs) {
|
|
211
|
+
const setup = s.hasSharedSetup ? " [shared setup]" : "";
|
|
212
|
+
const ann = s.flowAnnotations.length > 0 ? ` (flow: ${s.flowAnnotations.join(", ")})` : "";
|
|
213
|
+
parts.push(`- ${s.file}: "${s.name}" — ${s.framework}${setup}${ann}`);
|
|
214
|
+
const covered = [];
|
|
215
|
+
if (s.routesCovered.length > 0)
|
|
216
|
+
covered.push(`routes: ${s.routesCovered.join(", ")}`);
|
|
217
|
+
if (s.endpointsCovered.length > 0)
|
|
218
|
+
covered.push(`endpoints: ${s.endpointsCovered.join(", ")}`);
|
|
219
|
+
if (covered.length > 0)
|
|
220
|
+
parts.push(` - covers ${covered.join("; ")}`);
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
if (uncoveredRoutes.length > 0 || uncoveredEndpoints.length > 0) {
|
|
224
|
+
parts.push("");
|
|
225
|
+
parts.push("Coverage gaps (implicated but no covering spec):");
|
|
226
|
+
for (const r of uncoveredRoutes)
|
|
227
|
+
parts.push(`- ${r}`);
|
|
228
|
+
for (const e of uncoveredEndpoints)
|
|
229
|
+
parts.push(`- ${e}`);
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
parts.push("");
|
|
233
|
+
parts.push(`# Diff (${diff.base}..${diff.head})`);
|
|
234
|
+
if (targetUrl)
|
|
235
|
+
parts.push(`Deployed at: ${targetUrl}`);
|
|
236
|
+
parts.push("");
|
|
237
|
+
for (const f of diff.files) {
|
|
238
|
+
parts.push(renderFile(f));
|
|
239
|
+
}
|
|
240
|
+
parts.push("");
|
|
241
|
+
parts.push("Now produce the plan via the emit_plan tool.");
|
|
242
|
+
return parts.join("\n");
|
|
243
|
+
}
|
|
244
|
+
function endpointAnnotations(e) {
|
|
245
|
+
const parts = [];
|
|
246
|
+
if (e.bodyShape)
|
|
247
|
+
parts.push(`body: ${e.bodyShape}`);
|
|
248
|
+
const services = e.services ?? [];
|
|
249
|
+
if (services.length > 0)
|
|
250
|
+
parts.push(`services: ${services.join(", ")}`);
|
|
251
|
+
const tables = e.tables ?? [];
|
|
252
|
+
if (tables.length > 0)
|
|
253
|
+
parts.push(`tables: ${tables.join(", ")}`);
|
|
254
|
+
return parts.length > 0 ? ` (${parts.join("; ")})` : "";
|
|
255
|
+
}
|
|
256
|
+
function renderFile(f) {
|
|
257
|
+
const header = `## ${f.status.toUpperCase()} ${f.path}${f.oldPath ? ` (from ${f.oldPath})` : ""} +${f.additions}/-${f.deletions}${f.binary ? " [binary]" : ""}`;
|
|
258
|
+
if (f.binary || f.hunks.length === 0)
|
|
259
|
+
return header;
|
|
260
|
+
const hunks = f.hunks.map((h) => "```diff\n" + truncate(h, 4000) + "\n```").join("\n");
|
|
261
|
+
return `${header}\n${hunks}`;
|
|
262
|
+
}
|
|
263
|
+
function truncate(s, n) {
|
|
264
|
+
return s.length > n ? s.slice(0, n) + `\n... [${s.length - n} chars truncated]` : s;
|
|
265
|
+
}
|
|
266
|
+
//# sourceMappingURL=prompt.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"prompt.js","sourceRoot":"","sources":["../src/prompt.ts"],"names":[],"mappings":"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"}
|
package/dist/runner.d.ts
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import type { SelectionResult } from "./select.js";
|
|
2
|
+
export interface RunCommandOptions {
|
|
3
|
+
selection: SelectionResult;
|
|
4
|
+
/** Production URL to run the specs against. */
|
|
5
|
+
target: string;
|
|
6
|
+
/** Optional explicit Playwright config path (default: Playwright's own resolution). */
|
|
7
|
+
playwrightConfig?: string;
|
|
8
|
+
/**
|
|
9
|
+
* Where Playwright should write its JSON reporter output. Defaults to a path
|
|
10
|
+
* the CLI generates per-run.
|
|
11
|
+
*/
|
|
12
|
+
jsonReportPath: string;
|
|
13
|
+
/** Extra env vars to forward to the child process (merged on top of inherited env). */
|
|
14
|
+
extraEnv?: Record<string, string>;
|
|
15
|
+
}
|
|
16
|
+
export interface PlaywrightCommand {
|
|
17
|
+
command: string;
|
|
18
|
+
args: string[];
|
|
19
|
+
env: Record<string, string>;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Build the `npx playwright test` invocation that runs the selected specs
|
|
23
|
+
* against the target URL. Returns null if there is nothing to run (no covering
|
|
24
|
+
* specs of any kind in the selection).
|
|
25
|
+
*/
|
|
26
|
+
export declare function buildPlaywrightCommand(opts: RunCommandOptions): PlaywrightCommand | null;
|
|
27
|
+
export interface FailedTest {
|
|
28
|
+
file: string;
|
|
29
|
+
title: string;
|
|
30
|
+
error: string;
|
|
31
|
+
}
|
|
32
|
+
export interface PlaywrightReport {
|
|
33
|
+
passed: number;
|
|
34
|
+
failed: number;
|
|
35
|
+
flaky: number;
|
|
36
|
+
skipped: number;
|
|
37
|
+
durationMs: number;
|
|
38
|
+
totalTests: number;
|
|
39
|
+
failedTests: FailedTest[];
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Parse Playwright's JSON reporter output into a flat report. Handles the
|
|
43
|
+
* recursive suite/spec/test structure and unwraps the (often-deeply-nested)
|
|
44
|
+
* failed-test details we want to surface.
|
|
45
|
+
*/
|
|
46
|
+
export declare function parsePlaywrightReport(raw: unknown): PlaywrightReport;
|
|
47
|
+
export declare function formatRunMarkdown(args: {
|
|
48
|
+
selection: SelectionResult;
|
|
49
|
+
report: PlaywrightReport | null;
|
|
50
|
+
target: string;
|
|
51
|
+
/** True if we short-circuited because nothing was selected. */
|
|
52
|
+
nothingToRun: boolean;
|
|
53
|
+
}): string;
|
package/dist/runner.js
ADDED
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Build the `npx playwright test` invocation that runs the selected specs
|
|
3
|
+
* against the target URL. Returns null if there is nothing to run (no covering
|
|
4
|
+
* specs of any kind in the selection).
|
|
5
|
+
*/
|
|
6
|
+
export function buildPlaywrightCommand(opts) {
|
|
7
|
+
const playwrightSpecs = opts.selection.selected.filter((s) => s.framework === "playwright");
|
|
8
|
+
if (playwrightSpecs.length === 0)
|
|
9
|
+
return null;
|
|
10
|
+
const args = ["-y", "playwright", "test"];
|
|
11
|
+
// Anchor the run to the selected spec files. Even with --grep, narrowing to
|
|
12
|
+
// the specific files is faster (Playwright skips loading other test files).
|
|
13
|
+
for (const s of playwrightSpecs)
|
|
14
|
+
args.push(s.file);
|
|
15
|
+
if (opts.selection.playwrightGrep) {
|
|
16
|
+
args.push("--grep", opts.selection.playwrightGrep);
|
|
17
|
+
}
|
|
18
|
+
if (opts.playwrightConfig) {
|
|
19
|
+
args.push("--config", opts.playwrightConfig);
|
|
20
|
+
}
|
|
21
|
+
args.push("--reporter=line,json");
|
|
22
|
+
const env = {
|
|
23
|
+
PLAYWRIGHT_BASE_URL: opts.target,
|
|
24
|
+
PLAYWRIGHT_JSON_OUTPUT_FILE: opts.jsonReportPath,
|
|
25
|
+
PLAYWRIGHT_JSON_OUTPUT_NAME: opts.jsonReportPath,
|
|
26
|
+
...(opts.extraEnv ?? {}),
|
|
27
|
+
};
|
|
28
|
+
return { command: "npx", args, env };
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Parse Playwright's JSON reporter output into a flat report. Handles the
|
|
32
|
+
* recursive suite/spec/test structure and unwraps the (often-deeply-nested)
|
|
33
|
+
* failed-test details we want to surface.
|
|
34
|
+
*/
|
|
35
|
+
export function parsePlaywrightReport(raw) {
|
|
36
|
+
const r = (raw ?? {});
|
|
37
|
+
const stats = r.stats ?? {};
|
|
38
|
+
const failedTests = [];
|
|
39
|
+
walkSuites(r.suites ?? [], "", failedTests);
|
|
40
|
+
const passed = stats.expected ?? 0;
|
|
41
|
+
const failed = stats.unexpected ?? failedTests.length;
|
|
42
|
+
const flaky = stats.flaky ?? 0;
|
|
43
|
+
const skipped = stats.skipped ?? 0;
|
|
44
|
+
return {
|
|
45
|
+
passed,
|
|
46
|
+
failed,
|
|
47
|
+
flaky,
|
|
48
|
+
skipped,
|
|
49
|
+
durationMs: stats.duration ?? 0,
|
|
50
|
+
totalTests: passed + failed + flaky + skipped,
|
|
51
|
+
failedTests,
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
function walkSuites(suites, fileFromParent, failed) {
|
|
55
|
+
for (const suite of suites) {
|
|
56
|
+
const file = suite.file ?? fileFromParent;
|
|
57
|
+
for (const spec of suite.specs ?? []) {
|
|
58
|
+
const specFile = spec.file ?? file;
|
|
59
|
+
const title = spec.title ?? "(untitled)";
|
|
60
|
+
const tests = spec.tests ?? [];
|
|
61
|
+
const hasFailure = tests.some((t) => (t.results ?? []).some((res) => res.status === "failed" || res.status === "timedOut"));
|
|
62
|
+
if (!hasFailure)
|
|
63
|
+
continue;
|
|
64
|
+
const errMsg = tests
|
|
65
|
+
.flatMap((t) => t.results ?? [])
|
|
66
|
+
.map((res) => res.error?.message)
|
|
67
|
+
.filter((m) => Boolean(m))
|
|
68
|
+
.join("\n---\n");
|
|
69
|
+
failed.push({ file: specFile, title, error: errMsg || "(no error message captured)" });
|
|
70
|
+
}
|
|
71
|
+
if (suite.suites && suite.suites.length > 0) {
|
|
72
|
+
walkSuites(suite.suites, file, failed);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
export function formatRunMarkdown(args) {
|
|
77
|
+
const lines = [];
|
|
78
|
+
lines.push("## claudia — post-deploy verification");
|
|
79
|
+
lines.push("");
|
|
80
|
+
lines.push(`Target: \`${args.target}\``);
|
|
81
|
+
if (args.nothingToRun) {
|
|
82
|
+
lines.push("");
|
|
83
|
+
if (args.selection.selectedTestCount === 0 && args.selection.uncoveredRoutes.length === 0 && args.selection.uncoveredEndpoints.length === 0) {
|
|
84
|
+
lines.push("**Nothing to verify.** No covering specs and no coverage gaps for this diff.");
|
|
85
|
+
}
|
|
86
|
+
else if (args.selection.selectedTestCount === 0) {
|
|
87
|
+
lines.push("**No covering Playwright specs.** Coverage gaps detected — see `claudia select` for details.");
|
|
88
|
+
}
|
|
89
|
+
else {
|
|
90
|
+
lines.push("**Cypress-only selection.** Run `claudia select --files-only | xargs cypress run --spec` separately.");
|
|
91
|
+
}
|
|
92
|
+
return lines.join("\n");
|
|
93
|
+
}
|
|
94
|
+
if (!args.report) {
|
|
95
|
+
lines.push("");
|
|
96
|
+
lines.push("**Run did not complete.** Check the workflow logs for the underlying Playwright error.");
|
|
97
|
+
return lines.join("\n");
|
|
98
|
+
}
|
|
99
|
+
const r = args.report;
|
|
100
|
+
const verdict = r.failed === 0 ? "✅ Pass" : "❌ Fail";
|
|
101
|
+
lines.push("");
|
|
102
|
+
lines.push(`**${verdict}** — ${r.passed}/${r.totalTests} passed${r.flaky > 0 ? `, ${r.flaky} flaky` : ""}${r.skipped > 0 ? `, ${r.skipped} skipped` : ""}. ⏱ ${(r.durationMs / 1000).toFixed(1)}s`);
|
|
103
|
+
if (r.failed > 0) {
|
|
104
|
+
lines.push("");
|
|
105
|
+
lines.push("### Failures");
|
|
106
|
+
for (const t of r.failedTests) {
|
|
107
|
+
lines.push("");
|
|
108
|
+
lines.push(`**${t.file}** — \`${t.title}\``);
|
|
109
|
+
lines.push("```");
|
|
110
|
+
lines.push(truncate(t.error, 2000));
|
|
111
|
+
lines.push("```");
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
return lines.join("\n");
|
|
115
|
+
}
|
|
116
|
+
function truncate(s, n) {
|
|
117
|
+
return s.length > n ? s.slice(0, n) + `\n... [${s.length - n} chars truncated]` : s;
|
|
118
|
+
}
|
|
119
|
+
//# sourceMappingURL=runner.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"runner.js","sourceRoot":"","sources":["../src/runner.ts"],"names":[],"mappings":"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"}
|
package/dist/select.d.ts
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import type { SpecEntry } from "./types.js";
|
|
2
|
+
export interface SelectOptions {
|
|
3
|
+
rootDir: string;
|
|
4
|
+
base: string;
|
|
5
|
+
head: string;
|
|
6
|
+
refreshMap?: boolean;
|
|
7
|
+
}
|
|
8
|
+
export interface SelectedSpec {
|
|
9
|
+
framework: SpecEntry["framework"];
|
|
10
|
+
file: string;
|
|
11
|
+
/** Test names from this file that cover the diff. */
|
|
12
|
+
tests: string[];
|
|
13
|
+
/** Whether the file has a beforeAll / before() hook — selecting any test forces the whole file. */
|
|
14
|
+
hasSharedSetup: boolean;
|
|
15
|
+
}
|
|
16
|
+
export interface SelectionResult {
|
|
17
|
+
totalSpecs: number;
|
|
18
|
+
selected: SelectedSpec[];
|
|
19
|
+
selectedTestCount: number;
|
|
20
|
+
uncoveredRoutes: string[];
|
|
21
|
+
uncoveredEndpoints: string[];
|
|
22
|
+
/** Playwright --grep value: alternation of all selected test names, regex-escaped. */
|
|
23
|
+
playwrightGrep: string | null;
|
|
24
|
+
/** Cypress --spec pattern: comma-joined list of spec files. */
|
|
25
|
+
cypressSpecs: string;
|
|
26
|
+
}
|
|
27
|
+
export declare function runSelect(opts: SelectOptions): SelectionResult;
|
|
28
|
+
export declare function formatSelectionMarkdown(r: SelectionResult, args: {
|
|
29
|
+
base: string;
|
|
30
|
+
head: string;
|
|
31
|
+
}): string;
|
package/dist/select.js
ADDED
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import { readDiff } from "./diff.js";
|
|
2
|
+
import { loadOrBuildMap } from "./map.js";
|
|
3
|
+
import { filterMapForDiff } from "./prompt.js";
|
|
4
|
+
export function runSelect(opts) {
|
|
5
|
+
const diff = readDiff({ base: opts.base, head: opts.head, cwd: opts.rootDir });
|
|
6
|
+
const map = loadOrBuildMap({ rootDir: opts.rootDir, refresh: opts.refreshMap });
|
|
7
|
+
const filtered = filterMapForDiff(map, diff);
|
|
8
|
+
// Group selected SpecEntry rows by file. Each entry in `coveringSpecs` is per-test;
|
|
9
|
+
// multiple tests in the same file share the file's metadata, so we collapse.
|
|
10
|
+
const byFile = new Map();
|
|
11
|
+
for (const s of filtered.coveringSpecs) {
|
|
12
|
+
let entry = byFile.get(s.file);
|
|
13
|
+
if (!entry) {
|
|
14
|
+
entry = {
|
|
15
|
+
framework: s.framework,
|
|
16
|
+
file: s.file,
|
|
17
|
+
tests: [],
|
|
18
|
+
hasSharedSetup: s.hasSharedSetup,
|
|
19
|
+
};
|
|
20
|
+
byFile.set(s.file, entry);
|
|
21
|
+
}
|
|
22
|
+
entry.tests.push(s.name);
|
|
23
|
+
}
|
|
24
|
+
const selected = Array.from(byFile.values()).map((s) => ({
|
|
25
|
+
...s,
|
|
26
|
+
tests: Array.from(new Set(s.tests)).sort(),
|
|
27
|
+
}));
|
|
28
|
+
selected.sort((a, b) => a.file.localeCompare(b.file));
|
|
29
|
+
// For shared-setup files the whole describe block runs anyway, so don't bother
|
|
30
|
+
// emitting a grep — let the runner pick up everything in those files. We
|
|
31
|
+
// include them in `selected` so the user can see what got pulled in. The
|
|
32
|
+
// grep is specifically for `playwright test --grep`; Cypress is handled via
|
|
33
|
+
// `cypressSpecs` below.
|
|
34
|
+
const grepEligible = selected.flatMap((s) => s.framework === "playwright" && !s.hasSharedSetup ? s.tests : []);
|
|
35
|
+
const playwrightGrep = grepEligible.length === 0 ? null : grepEligible.map(escapeForRegex).join("|");
|
|
36
|
+
const cypressSpecs = selected
|
|
37
|
+
.filter((s) => s.framework === "cypress")
|
|
38
|
+
.map((s) => s.file)
|
|
39
|
+
.join(",");
|
|
40
|
+
return {
|
|
41
|
+
totalSpecs: (map.specs ?? []).length,
|
|
42
|
+
selected,
|
|
43
|
+
selectedTestCount: selected.reduce((n, s) => n + s.tests.length, 0),
|
|
44
|
+
uncoveredRoutes: filtered.uncoveredRoutes,
|
|
45
|
+
uncoveredEndpoints: filtered.uncoveredEndpoints,
|
|
46
|
+
playwrightGrep,
|
|
47
|
+
cypressSpecs,
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
function escapeForRegex(s) {
|
|
51
|
+
return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
52
|
+
}
|
|
53
|
+
export function formatSelectionMarkdown(r, args) {
|
|
54
|
+
const lines = [];
|
|
55
|
+
lines.push("## claudia — spec selection");
|
|
56
|
+
lines.push("");
|
|
57
|
+
lines.push(`Diff: \`${args.base}..${args.head}\` · ${r.totalSpecs} specs indexed · ${r.selectedTestCount} selected.`);
|
|
58
|
+
lines.push("");
|
|
59
|
+
if (r.selected.length === 0) {
|
|
60
|
+
lines.push("**No covering specs.** ");
|
|
61
|
+
if (r.uncoveredRoutes.length > 0 || r.uncoveredEndpoints.length > 0) {
|
|
62
|
+
lines.push("Coverage gaps detected — the diff implicates flows that no existing spec covers.");
|
|
63
|
+
lines.push("");
|
|
64
|
+
lines.push("### Uncovered flows");
|
|
65
|
+
for (const x of r.uncoveredRoutes)
|
|
66
|
+
lines.push(`- ${x}`);
|
|
67
|
+
for (const x of r.uncoveredEndpoints)
|
|
68
|
+
lines.push(`- ${x}`);
|
|
69
|
+
}
|
|
70
|
+
else {
|
|
71
|
+
lines.push("Nothing to verify against prod.");
|
|
72
|
+
}
|
|
73
|
+
return lines.join("\n");
|
|
74
|
+
}
|
|
75
|
+
lines.push("### Selected specs");
|
|
76
|
+
for (const s of r.selected) {
|
|
77
|
+
const setup = s.hasSharedSetup ? " · [shared setup — whole file runs]" : "";
|
|
78
|
+
lines.push(`- **${s.file}** (${s.framework})${setup}`);
|
|
79
|
+
for (const t of s.tests)
|
|
80
|
+
lines.push(` - \`${t}\``);
|
|
81
|
+
}
|
|
82
|
+
if (r.uncoveredRoutes.length > 0 || r.uncoveredEndpoints.length > 0) {
|
|
83
|
+
lines.push("");
|
|
84
|
+
lines.push("### Coverage gaps");
|
|
85
|
+
lines.push("Implicated by this diff, no covering spec — candidates for new tests:");
|
|
86
|
+
for (const x of r.uncoveredRoutes)
|
|
87
|
+
lines.push(`- ${x}`);
|
|
88
|
+
for (const x of r.uncoveredEndpoints)
|
|
89
|
+
lines.push(`- ${x}`);
|
|
90
|
+
}
|
|
91
|
+
lines.push("");
|
|
92
|
+
lines.push("### How to run");
|
|
93
|
+
if (r.playwrightGrep) {
|
|
94
|
+
lines.push("```bash");
|
|
95
|
+
lines.push(`npx playwright test --grep "${r.playwrightGrep}"`);
|
|
96
|
+
lines.push("```");
|
|
97
|
+
}
|
|
98
|
+
if (r.cypressSpecs) {
|
|
99
|
+
lines.push("```bash");
|
|
100
|
+
lines.push(`npx cypress run --spec "${r.cypressSpecs}"`);
|
|
101
|
+
lines.push("```");
|
|
102
|
+
}
|
|
103
|
+
if (!r.playwrightGrep && !r.cypressSpecs) {
|
|
104
|
+
lines.push("_All selected specs have shared setup; run the listed files directly._");
|
|
105
|
+
}
|
|
106
|
+
return lines.join("\n");
|
|
107
|
+
}
|
|
108
|
+
//# sourceMappingURL=select.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"select.js","sourceRoot":"","sources":["../src/select.ts"],"names":[],"mappings":"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"}
|