@testrelic/mcp 2.1.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 +201 -0
- package/README.md +198 -0
- package/dist/.tsbuildinfo +1 -0
- package/dist/cache/blob.d.ts +22 -0
- package/dist/cache/blob.d.ts.map +1 -0
- package/dist/cache/blob.js +92 -0
- package/dist/cache/blob.js.map +1 -0
- package/dist/cache/diff-reader.d.ts +29 -0
- package/dist/cache/diff-reader.d.ts.map +1 -0
- package/dist/cache/diff-reader.js +34 -0
- package/dist/cache/diff-reader.js.map +1 -0
- package/dist/cache/index.d.ts +57 -0
- package/dist/cache/index.d.ts.map +1 -0
- package/dist/cache/index.js +99 -0
- package/dist/cache/index.js.map +1 -0
- package/dist/cache/key.d.ts +15 -0
- package/dist/cache/key.d.ts.map +1 -0
- package/dist/cache/key.js +32 -0
- package/dist/cache/key.js.map +1 -0
- package/dist/cache/lru.d.ts +21 -0
- package/dist/cache/lru.d.ts.map +1 -0
- package/dist/cache/lru.js +32 -0
- package/dist/cache/lru.js.map +1 -0
- package/dist/cache/sqlite.d.ts +22 -0
- package/dist/cache/sqlite.d.ts.map +1 -0
- package/dist/cache/sqlite.js +102 -0
- package/dist/cache/sqlite.js.map +1 -0
- package/dist/cache/vector.d.ts +42 -0
- package/dist/cache/vector.d.ts.map +1 -0
- package/dist/cache/vector.js +187 -0
- package/dist/cache/vector.js.map +1 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +205 -0
- package/dist/cli.js.map +1 -0
- package/dist/clients/amplitude.d.ts +24 -0
- package/dist/clients/amplitude.d.ts.map +1 -0
- package/dist/clients/amplitude.js +15 -0
- package/dist/clients/amplitude.js.map +1 -0
- package/dist/clients/clickhouse.d.ts +10 -0
- package/dist/clients/clickhouse.d.ts.map +1 -0
- package/dist/clients/clickhouse.js +8 -0
- package/dist/clients/clickhouse.js.map +1 -0
- package/dist/clients/cloud.d.ts +347 -0
- package/dist/clients/cloud.d.ts.map +1 -0
- package/dist/clients/cloud.js +402 -0
- package/dist/clients/cloud.js.map +1 -0
- package/dist/clients/http.d.ts +40 -0
- package/dist/clients/http.d.ts.map +1 -0
- package/dist/clients/http.js +67 -0
- package/dist/clients/http.js.map +1 -0
- package/dist/clients/index.d.ts +38 -0
- package/dist/clients/index.d.ts.map +1 -0
- package/dist/clients/index.js +24 -0
- package/dist/clients/index.js.map +1 -0
- package/dist/clients/jira.d.ts +16 -0
- package/dist/clients/jira.d.ts.map +1 -0
- package/dist/clients/jira.js +11 -0
- package/dist/clients/jira.js.map +1 -0
- package/dist/clients/loki.d.ts +7 -0
- package/dist/clients/loki.d.ts.map +1 -0
- package/dist/clients/loki.js +8 -0
- package/dist/clients/loki.js.map +1 -0
- package/dist/clients/retry.d.ts +28 -0
- package/dist/clients/retry.d.ts.map +1 -0
- package/dist/clients/retry.js +79 -0
- package/dist/clients/retry.js.map +1 -0
- package/dist/clients/testrelic.d.ts +90 -0
- package/dist/clients/testrelic.d.ts.map +1 -0
- package/dist/clients/testrelic.js +68 -0
- package/dist/clients/testrelic.js.map +1 -0
- package/dist/config.d.ts +216 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +233 -0
- package/dist/config.js.map +1 -0
- package/dist/context/code-map.d.ts +35 -0
- package/dist/context/code-map.d.ts.map +1 -0
- package/dist/context/code-map.js +187 -0
- package/dist/context/code-map.js.map +1 -0
- package/dist/context/correlator.d.ts +32 -0
- package/dist/context/correlator.d.ts.map +1 -0
- package/dist/context/correlator.js +106 -0
- package/dist/context/correlator.js.map +1 -0
- package/dist/context/coverage-map.d.ts +25 -0
- package/dist/context/coverage-map.d.ts.map +1 -0
- package/dist/context/coverage-map.js +44 -0
- package/dist/context/coverage-map.js.map +1 -0
- package/dist/context/index.d.ts +20 -0
- package/dist/context/index.d.ts.map +1 -0
- package/dist/context/index.js +18 -0
- package/dist/context/index.js.map +1 -0
- package/dist/context/journey-graph.d.ts +22 -0
- package/dist/context/journey-graph.d.ts.map +1 -0
- package/dist/context/journey-graph.js +38 -0
- package/dist/context/journey-graph.js.map +1 -0
- package/dist/context/signal-map.d.ts +26 -0
- package/dist/context/signal-map.d.ts.map +1 -0
- package/dist/context/signal-map.js +30 -0
- package/dist/context/signal-map.js.map +1 -0
- package/dist/elicit/ask.d.ts +28 -0
- package/dist/elicit/ask.d.ts.map +1 -0
- package/dist/elicit/ask.js +31 -0
- package/dist/elicit/ask.js.map +1 -0
- package/dist/elicit/zod-to-json.d.ts +7 -0
- package/dist/elicit/zod-to-json.d.ts.map +1 -0
- package/dist/elicit/zod-to-json.js +37 -0
- package/dist/elicit/zod-to-json.js.map +1 -0
- package/dist/errors.d.ts +59 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +122 -0
- package/dist/errors.js.map +1 -0
- package/dist/index.d.ts +43 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +127 -0
- package/dist/index.js.map +1 -0
- package/dist/logger.d.ts +6 -0
- package/dist/logger.d.ts.map +1 -0
- package/dist/logger.js +29 -0
- package/dist/logger.js.map +1 -0
- package/dist/prompts/index.d.ts +8 -0
- package/dist/prompts/index.d.ts.map +1 -0
- package/dist/prompts/index.js +86 -0
- package/dist/prompts/index.js.map +1 -0
- package/dist/registry/index.d.ts +70 -0
- package/dist/registry/index.d.ts.map +1 -0
- package/dist/registry/index.js +88 -0
- package/dist/registry/index.js.map +1 -0
- package/dist/registry/project.d.ts +20 -0
- package/dist/registry/project.d.ts.map +1 -0
- package/dist/registry/project.js +44 -0
- package/dist/registry/project.js.map +1 -0
- package/dist/resources/index.d.ts +13 -0
- package/dist/resources/index.d.ts.map +1 -0
- package/dist/resources/index.js +76 -0
- package/dist/resources/index.js.map +1 -0
- package/dist/sampling/bridge.d.ts +35 -0
- package/dist/sampling/bridge.d.ts.map +1 -0
- package/dist/sampling/bridge.js +39 -0
- package/dist/sampling/bridge.js.map +1 -0
- package/dist/telemetry/metrics.d.ts +29 -0
- package/dist/telemetry/metrics.d.ts.map +1 -0
- package/dist/telemetry/metrics.js +46 -0
- package/dist/telemetry/metrics.js.map +1 -0
- package/dist/telemetry/tokens.d.ts +16 -0
- package/dist/telemetry/tokens.d.ts.map +1 -0
- package/dist/telemetry/tokens.js +40 -0
- package/dist/telemetry/tokens.js.map +1 -0
- package/dist/tools/core/index.d.ts +8 -0
- package/dist/tools/core/index.d.ts.map +1 -0
- package/dist/tools/core/index.js +219 -0
- package/dist/tools/core/index.js.map +1 -0
- package/dist/tools/coverage/index.d.ts +9 -0
- package/dist/tools/coverage/index.d.ts.map +1 -0
- package/dist/tools/coverage/index.js +247 -0
- package/dist/tools/coverage/index.js.map +1 -0
- package/dist/tools/creation/index.d.ts +9 -0
- package/dist/tools/creation/index.d.ts.map +1 -0
- package/dist/tools/creation/index.js +357 -0
- package/dist/tools/creation/index.js.map +1 -0
- package/dist/tools/creation/templates.d.ts +17 -0
- package/dist/tools/creation/templates.d.ts.map +1 -0
- package/dist/tools/creation/templates.js +63 -0
- package/dist/tools/creation/templates.js.map +1 -0
- package/dist/tools/devtools/index.d.ts +9 -0
- package/dist/tools/devtools/index.d.ts.map +1 -0
- package/dist/tools/devtools/index.js +106 -0
- package/dist/tools/devtools/index.js.map +1 -0
- package/dist/tools/healing/index.d.ts +10 -0
- package/dist/tools/healing/index.d.ts.map +1 -0
- package/dist/tools/healing/index.js +190 -0
- package/dist/tools/healing/index.js.map +1 -0
- package/dist/tools/impact/index.d.ts +8 -0
- package/dist/tools/impact/index.d.ts.map +1 -0
- package/dist/tools/impact/index.js +215 -0
- package/dist/tools/impact/index.js.map +1 -0
- package/dist/tools/index.d.ts +9 -0
- package/dist/tools/index.d.ts.map +1 -0
- package/dist/tools/index.js +29 -0
- package/dist/tools/index.js.map +1 -0
- package/dist/tools/signals/index.d.ts +9 -0
- package/dist/tools/signals/index.d.ts.map +1 -0
- package/dist/tools/signals/index.js +100 -0
- package/dist/tools/signals/index.js.map +1 -0
- package/dist/tools/triage/index.d.ts +9 -0
- package/dist/tools/triage/index.d.ts.map +1 -0
- package/dist/tools/triage/index.js +366 -0
- package/dist/tools/triage/index.js.map +1 -0
- package/dist/transport/http.d.ts +16 -0
- package/dist/transport/http.d.ts.map +1 -0
- package/dist/transport/http.js +110 -0
- package/dist/transport/http.js.map +1 -0
- package/dist/transport/stdio.d.ts +6 -0
- package/dist/transport/stdio.d.ts.map +1 -0
- package/dist/transport/stdio.js +19 -0
- package/dist/transport/stdio.js.map +1 -0
- package/dist/types/index.d.ts +268 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +6 -0
- package/dist/types/index.js.map +1 -0
- package/dist/version.d.ts +7 -0
- package/dist/version.d.ts.map +1 -0
- package/dist/version.js +7 -0
- package/dist/version.js.map +1 -0
- package/package.json +84 -0
|
@@ -0,0 +1,247 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
/**
|
|
3
|
+
* Coverage capability — the core intelligence behind the ≥95% User-Coverage
|
|
4
|
+
* / ≥95% Test-Coverage goal. Every tool is cache-first and uses the 3-state
|
|
5
|
+
* diff reader for repeat calls on the same project.
|
|
6
|
+
*/
|
|
7
|
+
export const coverageTools = [
|
|
8
|
+
{
|
|
9
|
+
name: "tr_user_journeys",
|
|
10
|
+
capability: "coverage",
|
|
11
|
+
title: "Top N Amplitude user journeys",
|
|
12
|
+
description: "Returns the top N user journeys for a project ordered by distinct users in the last 30 days. Uses L1+L2 cache with a 1h TTL.",
|
|
13
|
+
inputSchema: {
|
|
14
|
+
project_id: z.string(),
|
|
15
|
+
limit: z.number().int().min(1).max(200).optional().default(20),
|
|
16
|
+
},
|
|
17
|
+
outputSchema: {
|
|
18
|
+
journeys: z.array(z.object({
|
|
19
|
+
id: z.string(),
|
|
20
|
+
name: z.string(),
|
|
21
|
+
events: z.array(z.string()),
|
|
22
|
+
user_count: z.number(),
|
|
23
|
+
session_count: z.number(),
|
|
24
|
+
})),
|
|
25
|
+
total_users_tracked: z.number(),
|
|
26
|
+
},
|
|
27
|
+
handler: async (input, ctx) => {
|
|
28
|
+
const project_id = input.project_id;
|
|
29
|
+
const limit = input.limit ?? 20;
|
|
30
|
+
const journeys = await ctx.context.journeys.top(project_id, limit);
|
|
31
|
+
const total = journeys.reduce((s, j) => s + (j.user_count ?? 0), 0);
|
|
32
|
+
if (!journeys.length) {
|
|
33
|
+
return { text: `No journeys available for ${project_id}.`, structured: { journeys: [], total_users_tracked: 0 } };
|
|
34
|
+
}
|
|
35
|
+
const lines = [`## Top ${journeys.length} user journeys — ${project_id}`, "", `**Total users tracked:** ${total.toLocaleString()}`, ""];
|
|
36
|
+
for (const j of journeys) {
|
|
37
|
+
lines.push(`- **\`${j.id}\`** — ${j.name}`);
|
|
38
|
+
lines.push(` ${j.user_count.toLocaleString()} users · ${j.session_count.toLocaleString()} sessions · ${j.events.length} steps`);
|
|
39
|
+
lines.push(` ${j.events.join(" → ")}`);
|
|
40
|
+
}
|
|
41
|
+
return {
|
|
42
|
+
text: lines.join("\n"),
|
|
43
|
+
structured: {
|
|
44
|
+
journeys: journeys.map((j) => ({
|
|
45
|
+
id: j.id,
|
|
46
|
+
name: j.name,
|
|
47
|
+
events: j.events,
|
|
48
|
+
user_count: j.user_count,
|
|
49
|
+
session_count: j.session_count,
|
|
50
|
+
})),
|
|
51
|
+
total_users_tracked: total,
|
|
52
|
+
},
|
|
53
|
+
};
|
|
54
|
+
},
|
|
55
|
+
},
|
|
56
|
+
{
|
|
57
|
+
name: "tr_test_map",
|
|
58
|
+
capability: "coverage",
|
|
59
|
+
title: "Test-to-journey/code-node map",
|
|
60
|
+
description: "Returns the test coverage map for a project — every test_id with the journeys and code nodes it exercises. Large responses are written to the blob store and summarised.",
|
|
61
|
+
inputSchema: {
|
|
62
|
+
project_id: z.string(),
|
|
63
|
+
test_id: z.string().optional(),
|
|
64
|
+
},
|
|
65
|
+
handler: async (input, ctx) => {
|
|
66
|
+
const project_id = input.project_id;
|
|
67
|
+
const test_id = input.test_id;
|
|
68
|
+
const all = await ctx.context.coverage.load(project_id);
|
|
69
|
+
const filtered = test_id ? all.filter((t) => t.test_id === test_id) : all;
|
|
70
|
+
if (!filtered.length) {
|
|
71
|
+
return { text: `No coverage entries found for ${project_id}${test_id ? ` / ${test_id}` : ""}.`, structured: { entries: [] } };
|
|
72
|
+
}
|
|
73
|
+
const lines = [`## Test map — ${project_id}${test_id ? ` / ${test_id}` : ""}`, "", `**Entries:** ${filtered.length}`, ""];
|
|
74
|
+
for (const e of filtered.slice(0, 50)) {
|
|
75
|
+
lines.push(`- **\`${e.test_id}\`** — ${e.test_name} (${e.suite})`);
|
|
76
|
+
lines.push(` journeys: ${e.journey_ids.join(", ") || "(none)"}`);
|
|
77
|
+
lines.push(` code_nodes: ${e.code_node_ids.slice(0, 5).join(", ")}${e.code_node_ids.length > 5 ? ` (+${e.code_node_ids.length - 5} more)` : ""}`);
|
|
78
|
+
}
|
|
79
|
+
if (filtered.length > 50)
|
|
80
|
+
lines.push(`_…and ${filtered.length - 50} more entries_`);
|
|
81
|
+
const cache_key = ctx.cache.key("tr_test_map", { project_id, test_id });
|
|
82
|
+
const sha = ctx.cache.blob.write(JSON.stringify(filtered));
|
|
83
|
+
ctx.cache.set(cache_key, { blob: sha }, { ttlSeconds: 900 });
|
|
84
|
+
return { text: lines.join("\n"), structured: { entries: filtered, cache_key }, cacheKey: cache_key };
|
|
85
|
+
},
|
|
86
|
+
},
|
|
87
|
+
{
|
|
88
|
+
name: "tr_coverage_gaps",
|
|
89
|
+
capability: "coverage",
|
|
90
|
+
title: "Ranked coverage gaps",
|
|
91
|
+
description: "Returns the top-N user journeys with NO test covering them, ordered by user count. Each gap includes the pp coverage gain we'd get by covering it and any partial overlaps with existing tests.",
|
|
92
|
+
inputSchema: {
|
|
93
|
+
project_id: z.string(),
|
|
94
|
+
limit: z.number().int().min(1).max(50).optional().default(10),
|
|
95
|
+
},
|
|
96
|
+
outputSchema: {
|
|
97
|
+
gaps: z.array(z.object({
|
|
98
|
+
journey_id: z.string(),
|
|
99
|
+
journey_name: z.string(),
|
|
100
|
+
user_count: z.number(),
|
|
101
|
+
pp_coverage_gain: z.number(),
|
|
102
|
+
events: z.array(z.string()),
|
|
103
|
+
})),
|
|
104
|
+
},
|
|
105
|
+
handler: async (input, ctx) => {
|
|
106
|
+
const project_id = input.project_id;
|
|
107
|
+
const limit = input.limit ?? 10;
|
|
108
|
+
const gaps = await ctx.context.correlator.rankedGaps(project_id, limit);
|
|
109
|
+
if (!gaps.length) {
|
|
110
|
+
return { text: `🎉 No coverage gaps in top journeys for ${project_id}.`, structured: { gaps: [] } };
|
|
111
|
+
}
|
|
112
|
+
const lines = [`## Coverage gaps — ${project_id}`, "", `**Top ${gaps.length} uncovered journeys (by users):**`, ""];
|
|
113
|
+
for (const g of gaps) {
|
|
114
|
+
lines.push(`- **\`${g.journey_id}\`** — ${g.journey_name}`);
|
|
115
|
+
lines.push(` ${g.user_count.toLocaleString()} users · +${g.pp_coverage_gain.toFixed(1)}pp user coverage if covered`);
|
|
116
|
+
lines.push(` ${g.events.join(" → ")}`);
|
|
117
|
+
if (g.partial_overlaps?.length) {
|
|
118
|
+
lines.push(` partial overlaps: ${g.partial_overlaps.map((o) => `${o.test_id}(${(o.overlap * 100).toFixed(0)}%)`).join(", ")}`);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
lines.push("");
|
|
122
|
+
lines.push("Next step: call `tr_plan_test` with the highest-gain `journey_id` to draft a plan.");
|
|
123
|
+
return { text: lines.join("\n"), structured: { gaps } };
|
|
124
|
+
},
|
|
125
|
+
},
|
|
126
|
+
{
|
|
127
|
+
name: "tr_coverage_report",
|
|
128
|
+
capability: "coverage",
|
|
129
|
+
title: "Coverage report (95% readout)",
|
|
130
|
+
description: "Returns user_coverage and test_coverage metrics with progress toward the 95/95 targets. Repeat calls return a 3-state diff (unchanged / diff / full) to cut token usage on iteration.",
|
|
131
|
+
inputSchema: {
|
|
132
|
+
project_id: z.string(),
|
|
133
|
+
read_mode: z.enum(["auto", "full"]).optional().default("auto"),
|
|
134
|
+
},
|
|
135
|
+
outputSchema: {
|
|
136
|
+
user_coverage: z.number(),
|
|
137
|
+
test_coverage: z.number(),
|
|
138
|
+
meets_95_user: z.boolean(),
|
|
139
|
+
meets_95_test: z.boolean(),
|
|
140
|
+
},
|
|
141
|
+
handler: async (input, ctx) => {
|
|
142
|
+
const project_id = input.project_id;
|
|
143
|
+
const mode = input.read_mode ?? "auto";
|
|
144
|
+
const report = await ctx.context.correlator.coverageReport(project_id);
|
|
145
|
+
const correlation = await ctx.context.correlator.correlate(project_id);
|
|
146
|
+
const meets_user = report.user_coverage >= 0.95;
|
|
147
|
+
const meets_test = report.test_coverage >= 0.95;
|
|
148
|
+
const fullText = [
|
|
149
|
+
`## Coverage report — ${project_id}`,
|
|
150
|
+
`_Generated at ${report.generated_at}_`,
|
|
151
|
+
"",
|
|
152
|
+
`| Metric | Value | Target | Status |`,
|
|
153
|
+
`|---|---|---|---|`,
|
|
154
|
+
`| User coverage (unweighted) | ${(report.user_coverage * 100).toFixed(1)}% | 95% | ${meets_user ? "✅" : "⚠️"} |`,
|
|
155
|
+
`| User coverage (weighted by users) | ${(correlation.user_coverage_weighted * 100).toFixed(1)}% | 95% | ${correlation.user_coverage_weighted >= 0.95 ? "✅" : "⚠️"} |`,
|
|
156
|
+
`| Test coverage (code nodes) | ${(report.test_coverage * 100).toFixed(1)}% | 95% | ${meets_test ? "✅" : "⚠️"} |`,
|
|
157
|
+
"",
|
|
158
|
+
`**Total journeys tracked:** ${report.total_journeys} (${report.covered_journeys} covered, ${report.uncovered_journeys} uncovered)`,
|
|
159
|
+
`**Total users tracked:** ${correlation.total_users_tracked.toLocaleString()} (${correlation.total_users_covered.toLocaleString()} covered)`,
|
|
160
|
+
"",
|
|
161
|
+
report.gaps_summary.length > 0
|
|
162
|
+
? "### Top gaps by user count\n" + report.gaps_summary.map((g) => `- \`${g.journey_id}\` (${g.user_count.toLocaleString()} users) — ${g.reason}`).join("\n")
|
|
163
|
+
: "_No gaps in top journeys._",
|
|
164
|
+
"",
|
|
165
|
+
"Next step: call `tr_coverage_gaps` to see the ranked list or `tr_plan_test` on a specific journey.",
|
|
166
|
+
].join("\n");
|
|
167
|
+
if (mode === "full") {
|
|
168
|
+
return {
|
|
169
|
+
text: fullText,
|
|
170
|
+
structured: {
|
|
171
|
+
report,
|
|
172
|
+
user_coverage: report.user_coverage,
|
|
173
|
+
test_coverage: report.test_coverage,
|
|
174
|
+
meets_95_user: meets_user,
|
|
175
|
+
meets_95_test: meets_test,
|
|
176
|
+
},
|
|
177
|
+
};
|
|
178
|
+
}
|
|
179
|
+
const state = ctx.cache.diff.read(`coverage-report:${project_id}`, fullText);
|
|
180
|
+
if (state.state === "unchanged") {
|
|
181
|
+
return {
|
|
182
|
+
text: [`## Coverage report — ${project_id}`, "", `_Unchanged since the last read (fingerprint ${state.fingerprint}). Call with read_mode=full to force a fresh read._`].join("\n"),
|
|
183
|
+
structured: {
|
|
184
|
+
report,
|
|
185
|
+
user_coverage: report.user_coverage,
|
|
186
|
+
test_coverage: report.test_coverage,
|
|
187
|
+
meets_95_user: meets_user,
|
|
188
|
+
meets_95_test: meets_test,
|
|
189
|
+
read_state: "unchanged",
|
|
190
|
+
},
|
|
191
|
+
};
|
|
192
|
+
}
|
|
193
|
+
if (state.state === "diff") {
|
|
194
|
+
return {
|
|
195
|
+
text: [`## Coverage report — ${project_id} (diff since last read)`, "", "```diff", state.content, "```"].join("\n"),
|
|
196
|
+
structured: {
|
|
197
|
+
report,
|
|
198
|
+
user_coverage: report.user_coverage,
|
|
199
|
+
test_coverage: report.test_coverage,
|
|
200
|
+
meets_95_user: meets_user,
|
|
201
|
+
meets_95_test: meets_test,
|
|
202
|
+
read_state: "diff",
|
|
203
|
+
},
|
|
204
|
+
};
|
|
205
|
+
}
|
|
206
|
+
return {
|
|
207
|
+
text: fullText,
|
|
208
|
+
structured: {
|
|
209
|
+
report,
|
|
210
|
+
user_coverage: report.user_coverage,
|
|
211
|
+
test_coverage: report.test_coverage,
|
|
212
|
+
meets_95_user: meets_user,
|
|
213
|
+
meets_95_test: meets_test,
|
|
214
|
+
read_state: "full",
|
|
215
|
+
},
|
|
216
|
+
};
|
|
217
|
+
},
|
|
218
|
+
},
|
|
219
|
+
{
|
|
220
|
+
name: "tr_fetch_cached",
|
|
221
|
+
capability: "coverage",
|
|
222
|
+
title: "Fetch a cached full payload",
|
|
223
|
+
description: "Fetches a payload referenced by a cache_key returned from another tool. Used to opt into large content only when needed (token efficiency).",
|
|
224
|
+
inputSchema: {
|
|
225
|
+
cache_key: z.string(),
|
|
226
|
+
},
|
|
227
|
+
handler: async (input, ctx) => {
|
|
228
|
+
const cache_key = input.cache_key;
|
|
229
|
+
const hit = ctx.cache.get(cache_key);
|
|
230
|
+
if (!hit)
|
|
231
|
+
return { text: `No cached value for key ${cache_key}.`, structured: {} };
|
|
232
|
+
const value = hit.value;
|
|
233
|
+
if (value.blob) {
|
|
234
|
+
const text = ctx.cache.blob.readText(value.blob);
|
|
235
|
+
if (!text)
|
|
236
|
+
return { text: `Blob ${value.blob} missing from L4.`, structured: {} };
|
|
237
|
+
return { text: ["```json", text.slice(0, 12_000), "```"].join("\n"), structured: { blob: value.blob, cache_key } };
|
|
238
|
+
}
|
|
239
|
+
return { text: ["```json", JSON.stringify(value, null, 2), "```"].join("\n"), structured: { value, cache_key } };
|
|
240
|
+
},
|
|
241
|
+
},
|
|
242
|
+
];
|
|
243
|
+
export function registerCoverageTools(ctx, register) {
|
|
244
|
+
for (const t of coverageTools)
|
|
245
|
+
register(t);
|
|
246
|
+
}
|
|
247
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/tools/coverage/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { ToolContext, ToolDefinition } from "../../registry/index.js";
|
|
2
|
+
/**
|
|
3
|
+
* Creation capability — Planner, Generator, DryRun, AssertionHelper,
|
|
4
|
+
* TemplateCatalog. Uses sampling for LLM-driven synthesis with deterministic
|
|
5
|
+
* fallbacks so the tools always return something useful even offline.
|
|
6
|
+
*/
|
|
7
|
+
export declare const creationTools: ToolDefinition[];
|
|
8
|
+
export declare function registerCreationTools(ctx: ToolContext, register: (def: ToolDefinition) => void): void;
|
|
9
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/tools/creation/index.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,WAAW,EAAE,cAAc,EAAE,MAAM,yBAAyB,CAAC;AAO3E;;;;GAIG;AAEH,eAAO,MAAM,aAAa,EAAE,cAAc,EAgWzC,CAAC;AAEF,wBAAgB,qBAAqB,CAAC,GAAG,EAAE,WAAW,EAAE,QAAQ,EAAE,CAAC,GAAG,EAAE,cAAc,KAAK,IAAI,GAAG,IAAI,CAErG"}
|
|
@@ -0,0 +1,357 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { execFile } from "node:child_process";
|
|
3
|
+
import { writeFileSync, mkdirSync } from "node:fs";
|
|
4
|
+
import { join } from "node:path";
|
|
5
|
+
import { promisify } from "node:util";
|
|
6
|
+
import { TEMPLATES } from "./templates.js";
|
|
7
|
+
import { NotFoundError } from "../../errors.js";
|
|
8
|
+
const execFileAsync = promisify(execFile);
|
|
9
|
+
/**
|
|
10
|
+
* Creation capability — Planner, Generator, DryRun, AssertionHelper,
|
|
11
|
+
* TemplateCatalog. Uses sampling for LLM-driven synthesis with deterministic
|
|
12
|
+
* fallbacks so the tools always return something useful even offline.
|
|
13
|
+
*/
|
|
14
|
+
export const creationTools = [
|
|
15
|
+
{
|
|
16
|
+
name: "tr_list_templates",
|
|
17
|
+
capability: "creation",
|
|
18
|
+
title: "List framework templates",
|
|
19
|
+
description: "Returns available test framework templates (Playwright, Cypress, Jest, Vitest).",
|
|
20
|
+
inputSchema: {},
|
|
21
|
+
handler: async () => {
|
|
22
|
+
const lines = ["## Test Framework Templates", ""];
|
|
23
|
+
const entries = Object.values(TEMPLATES).map((t) => ({
|
|
24
|
+
framework: t.framework,
|
|
25
|
+
extension: t.extension,
|
|
26
|
+
description: t.description,
|
|
27
|
+
}));
|
|
28
|
+
for (const t of entries)
|
|
29
|
+
lines.push(`- **${t.framework}** (\`${t.extension}\`) — ${t.description}`);
|
|
30
|
+
return { text: lines.join("\n"), structured: { templates: entries } };
|
|
31
|
+
},
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
name: "tr_plan_test",
|
|
35
|
+
capability: "creation",
|
|
36
|
+
title: "Planner — design a test plan",
|
|
37
|
+
description: "Produces a Markdown test plan for a journey gap. Input either a journey_id (preferred) or a freeform goal. Missing PRD/acceptance info is requested via elicitation; the result is cache-keyed on the journey signature.",
|
|
38
|
+
inputSchema: {
|
|
39
|
+
project_id: z.string(),
|
|
40
|
+
journey_id: z.string().optional(),
|
|
41
|
+
goal: z.string().optional().describe("Freeform goal if journey_id is unknown"),
|
|
42
|
+
framework: z.enum(["playwright", "cypress", "jest", "vitest"]).optional().default("playwright"),
|
|
43
|
+
prd: z.string().optional().describe("PRD or acceptance criteria. If omitted, we'll try to elicit it."),
|
|
44
|
+
},
|
|
45
|
+
outputSchema: {
|
|
46
|
+
plan: z.object({
|
|
47
|
+
journey_id: z.string().optional(),
|
|
48
|
+
goal: z.string(),
|
|
49
|
+
framework: z.string(),
|
|
50
|
+
steps: z.array(z.object({ step: z.number(), action: z.string(), expectation: z.string() })),
|
|
51
|
+
data_requirements: z.array(z.string()).optional(),
|
|
52
|
+
}),
|
|
53
|
+
cache_key: z.string(),
|
|
54
|
+
},
|
|
55
|
+
handler: async (input, ctx) => {
|
|
56
|
+
const project_id = input.project_id;
|
|
57
|
+
const journey_id = input.journey_id;
|
|
58
|
+
const framework = input.framework ?? "playwright";
|
|
59
|
+
let goal = input.goal;
|
|
60
|
+
let prd = input.prd;
|
|
61
|
+
let journey;
|
|
62
|
+
if (journey_id) {
|
|
63
|
+
journey = await ctx.context.journeys.byId(project_id, journey_id);
|
|
64
|
+
if (!journey)
|
|
65
|
+
throw new NotFoundError(`Journey ${journey_id} not found for ${project_id}`);
|
|
66
|
+
goal = goal ?? `Cover the user journey "${journey.name}" end-to-end in ${framework}.`;
|
|
67
|
+
}
|
|
68
|
+
if (!goal) {
|
|
69
|
+
return { text: "No goal or journey_id provided. Pass `goal` or `journey_id`.", structured: {} };
|
|
70
|
+
}
|
|
71
|
+
if (!prd) {
|
|
72
|
+
const elicited = await ctx.elicit.ask({
|
|
73
|
+
message: `Any PRD, acceptance criteria, or edge cases for "${goal}"? (optional)`,
|
|
74
|
+
schema: z.object({ prd: z.string().optional() }),
|
|
75
|
+
});
|
|
76
|
+
if (elicited.kind === "accepted" && typeof elicited.content.prd === "string")
|
|
77
|
+
prd = elicited.content.prd;
|
|
78
|
+
}
|
|
79
|
+
const cache_key = ctx.cache.key("tr_plan_test", { project_id, journey_id, goal, framework, prd });
|
|
80
|
+
const hit = ctx.cache.get(cache_key);
|
|
81
|
+
if (hit) {
|
|
82
|
+
return { text: hit.value.text, structured: { plan: hit.value.plan, cache_key }, cacheKey: cache_key };
|
|
83
|
+
}
|
|
84
|
+
// Try sampling; fall back to deterministic skeleton.
|
|
85
|
+
const samplingPrompt = [
|
|
86
|
+
`Design a ${framework} test plan that covers the following journey.`,
|
|
87
|
+
journey ? `Journey path: ${journey.events.join(" → ")}` : "",
|
|
88
|
+
`Goal: ${goal}`,
|
|
89
|
+
prd ? `PRD / acceptance criteria:\n${prd}` : "",
|
|
90
|
+
"",
|
|
91
|
+
"Respond with a JSON object matching:",
|
|
92
|
+
'{ "steps": [{ "step": 1, "action": "...", "expectation": "..." }], "data_requirements": ["..."] }',
|
|
93
|
+
]
|
|
94
|
+
.filter(Boolean)
|
|
95
|
+
.join("\n");
|
|
96
|
+
const sampled = await ctx.sampling.createMessage(samplingPrompt, {
|
|
97
|
+
systemPrompt: "You are a senior test engineer writing thorough, deterministic end-to-end tests. Prefer stable locators and explicit assertions.",
|
|
98
|
+
maxTokens: 800,
|
|
99
|
+
temperature: 0.2,
|
|
100
|
+
});
|
|
101
|
+
const plan = {
|
|
102
|
+
journey_id,
|
|
103
|
+
goal,
|
|
104
|
+
framework,
|
|
105
|
+
steps: [],
|
|
106
|
+
};
|
|
107
|
+
if (!sampled.fallback && sampled.text) {
|
|
108
|
+
try {
|
|
109
|
+
const m = sampled.text.match(/\{[\s\S]*\}/);
|
|
110
|
+
if (m) {
|
|
111
|
+
const parsed = JSON.parse(m[0]);
|
|
112
|
+
if (Array.isArray(parsed.steps))
|
|
113
|
+
plan.steps = parsed.steps;
|
|
114
|
+
if (Array.isArray(parsed.data_requirements))
|
|
115
|
+
plan.data_requirements = parsed.data_requirements;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
catch {
|
|
119
|
+
// fall through to skeleton
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
if (plan.steps.length === 0 && journey) {
|
|
123
|
+
plan.steps = journey.events.map((event, i) => ({
|
|
124
|
+
step: i + 1,
|
|
125
|
+
action: `Trigger event "${event}"`,
|
|
126
|
+
expectation: `Expect the user to reach the next step of ${journey.name}`,
|
|
127
|
+
}));
|
|
128
|
+
}
|
|
129
|
+
if (plan.steps.length === 0) {
|
|
130
|
+
plan.steps = [
|
|
131
|
+
{ step: 1, action: "Navigate to the target page", expectation: "Page loads successfully" },
|
|
132
|
+
{ step: 2, action: `Perform the action implied by: ${goal}`, expectation: "Expected outcome visible" },
|
|
133
|
+
{ step: 3, action: "Assert the final state", expectation: "State matches acceptance criteria" },
|
|
134
|
+
];
|
|
135
|
+
}
|
|
136
|
+
const text = [
|
|
137
|
+
`## Test Plan — ${plan.goal}`,
|
|
138
|
+
journey ? `**Journey:** \`${journey.id}\` · ${journey.events.join(" → ")}` : "",
|
|
139
|
+
`**Framework:** ${plan.framework}`,
|
|
140
|
+
"",
|
|
141
|
+
"### Steps",
|
|
142
|
+
...plan.steps.map((s) => `${s.step}. **${s.action}** → _${s.expectation}_`),
|
|
143
|
+
plan.data_requirements && plan.data_requirements.length ? `\n### Data requirements\n${plan.data_requirements.map((d) => `- ${d}`).join("\n")}` : "",
|
|
144
|
+
"",
|
|
145
|
+
"Next step: call `tr_generate_test` with this plan to produce runnable code.",
|
|
146
|
+
]
|
|
147
|
+
.filter(Boolean)
|
|
148
|
+
.join("\n");
|
|
149
|
+
ctx.cache.set(cache_key, { plan, text }, { ttlSeconds: 3600 });
|
|
150
|
+
return { text, structured: { plan, cache_key }, cacheKey: cache_key };
|
|
151
|
+
},
|
|
152
|
+
},
|
|
153
|
+
{
|
|
154
|
+
name: "tr_generate_test",
|
|
155
|
+
capability: "creation",
|
|
156
|
+
title: "Generator — produce runnable test code",
|
|
157
|
+
description: "Generates runnable test code (Playwright by default) from a plan. Uses sampling for synthesis with a deterministic template fallback. Writes to {outputDir}/generated/ and returns both the code and a cache_key.",
|
|
158
|
+
inputSchema: {
|
|
159
|
+
project_id: z.string(),
|
|
160
|
+
plan_cache_key: z.string().optional().describe("Cache key returned by tr_plan_test"),
|
|
161
|
+
plan: z
|
|
162
|
+
.object({
|
|
163
|
+
journey_id: z.string().optional(),
|
|
164
|
+
goal: z.string(),
|
|
165
|
+
framework: z.enum(["playwright", "cypress", "jest", "vitest"]),
|
|
166
|
+
steps: z.array(z.object({ step: z.number(), action: z.string(), expectation: z.string() })),
|
|
167
|
+
data_requirements: z.array(z.string()).optional(),
|
|
168
|
+
})
|
|
169
|
+
.optional(),
|
|
170
|
+
file_name: z.string().optional(),
|
|
171
|
+
},
|
|
172
|
+
outputSchema: {
|
|
173
|
+
file_path: z.string(),
|
|
174
|
+
framework: z.string(),
|
|
175
|
+
cache_key: z.string(),
|
|
176
|
+
code: z.string(),
|
|
177
|
+
},
|
|
178
|
+
handler: async (input, ctx) => {
|
|
179
|
+
let plan = input.plan;
|
|
180
|
+
if (!plan && input.plan_cache_key) {
|
|
181
|
+
const cached = ctx.cache.get(input.plan_cache_key);
|
|
182
|
+
if (cached)
|
|
183
|
+
plan = cached.value.plan;
|
|
184
|
+
}
|
|
185
|
+
if (!plan) {
|
|
186
|
+
return {
|
|
187
|
+
text: "No plan found. Pass a `plan` object directly or a `plan_cache_key` from tr_plan_test.",
|
|
188
|
+
structured: {},
|
|
189
|
+
};
|
|
190
|
+
}
|
|
191
|
+
const template = TEMPLATES[plan.framework];
|
|
192
|
+
if (!template) {
|
|
193
|
+
return { text: `Framework "${plan.framework}" has no template. Try playwright, cypress, jest, or vitest.`, structured: {} };
|
|
194
|
+
}
|
|
195
|
+
const file_name = input.file_name ?? `generated-${plan.journey_id ?? Date.now()}${template.extension}`;
|
|
196
|
+
const outDir = join(ctx.config.outputDir, "generated");
|
|
197
|
+
mkdirSync(outDir, { recursive: true });
|
|
198
|
+
const file_path = join(outDir, file_name);
|
|
199
|
+
const samplingPrompt = [
|
|
200
|
+
`Write ${plan.framework} test code for the following plan:`,
|
|
201
|
+
`Goal: ${plan.goal}`,
|
|
202
|
+
"Steps:",
|
|
203
|
+
...plan.steps.map((s) => ` ${s.step}. ${s.action} → expect: ${s.expectation}`),
|
|
204
|
+
plan.data_requirements?.length ? `Data: ${plan.data_requirements.join("; ")}` : "",
|
|
205
|
+
"",
|
|
206
|
+
`Return ONLY the body of the test function — one line per action. Use stable locators (getByRole, getByTestId). No preamble, no imports, no \`test(...)\` wrapper.`,
|
|
207
|
+
]
|
|
208
|
+
.filter(Boolean)
|
|
209
|
+
.join("\n");
|
|
210
|
+
const sampled = await ctx.sampling.createMessage(samplingPrompt, {
|
|
211
|
+
systemPrompt: "You are a senior QA engineer. Write deterministic test code with clear assertions.",
|
|
212
|
+
maxTokens: 1_200,
|
|
213
|
+
temperature: 0.15,
|
|
214
|
+
});
|
|
215
|
+
const steps = [];
|
|
216
|
+
if (!sampled.fallback && sampled.text.trim()) {
|
|
217
|
+
for (const line of sampled.text.split(/\r?\n/)) {
|
|
218
|
+
const trimmed = line.trim();
|
|
219
|
+
if (!trimmed)
|
|
220
|
+
continue;
|
|
221
|
+
if (trimmed.startsWith("```"))
|
|
222
|
+
continue;
|
|
223
|
+
steps.push(trimmed);
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
if (!steps.length) {
|
|
227
|
+
for (const s of plan.steps) {
|
|
228
|
+
if (plan.framework === "playwright") {
|
|
229
|
+
steps.push(`// Step ${s.step}: ${s.action}`);
|
|
230
|
+
steps.push(`await test.step("${s.action.replace(/"/g, '\\"')}", async () => { /* TODO: ${s.expectation} */ });`);
|
|
231
|
+
}
|
|
232
|
+
else if (plan.framework === "cypress") {
|
|
233
|
+
steps.push(`cy.log("${s.action.replace(/"/g, '\\"')}"); // TODO: ${s.expectation}`);
|
|
234
|
+
}
|
|
235
|
+
else {
|
|
236
|
+
steps.push(`// Step ${s.step}: ${s.action} — expect: ${s.expectation}`);
|
|
237
|
+
steps.push(`expect(true).toBe(true);`);
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
const code = template.skeleton({ testName: plan.goal, steps });
|
|
242
|
+
writeFileSync(file_path, code, "utf-8");
|
|
243
|
+
const cache_key = ctx.cache.key("tr_generate_test", { plan, file_name });
|
|
244
|
+
const sha = ctx.cache.blob.write(code);
|
|
245
|
+
ctx.cache.set(cache_key, { file_path, framework: plan.framework, blob: sha }, { ttlSeconds: 3_600 });
|
|
246
|
+
const text = [
|
|
247
|
+
`## Generated ${plan.framework} test`,
|
|
248
|
+
`**File:** \`${file_path}\``,
|
|
249
|
+
`**Fallback used:** ${sampled.fallback ? "yes (no sampling client)" : "no"}`,
|
|
250
|
+
"",
|
|
251
|
+
"```" + (plan.framework === "cypress" ? "ts" : "ts"),
|
|
252
|
+
code,
|
|
253
|
+
"```",
|
|
254
|
+
"",
|
|
255
|
+
"Next step: call `tr_dry_run_test` with this file path to verify it compiles.",
|
|
256
|
+
].join("\n");
|
|
257
|
+
return {
|
|
258
|
+
text,
|
|
259
|
+
structured: { file_path, framework: plan.framework, cache_key, code },
|
|
260
|
+
cacheKey: cache_key,
|
|
261
|
+
};
|
|
262
|
+
},
|
|
263
|
+
},
|
|
264
|
+
{
|
|
265
|
+
name: "tr_dry_run_test",
|
|
266
|
+
capability: "creation",
|
|
267
|
+
title: "Dry-run: tsc + framework list",
|
|
268
|
+
description: "Type-checks the generated file (`tsc --noEmit`) and lists tests (`playwright test --list` when applicable). Returns first-pass errors so the agent can iterate before committing.",
|
|
269
|
+
inputSchema: {
|
|
270
|
+
file_path: z.string().describe("Path to the generated test file"),
|
|
271
|
+
framework: z.enum(["playwright", "cypress", "jest", "vitest"]).optional().default("playwright"),
|
|
272
|
+
},
|
|
273
|
+
handler: async (input, ctx) => {
|
|
274
|
+
const file = input.file_path;
|
|
275
|
+
const framework = input.framework ?? "playwright";
|
|
276
|
+
const results = [];
|
|
277
|
+
try {
|
|
278
|
+
const { stdout, stderr } = await execFileAsync("npx", ["--yes", "tsc", "--noEmit", "--skipLibCheck", file], {
|
|
279
|
+
cwd: process.cwd(),
|
|
280
|
+
timeout: ctx.config.timeouts.analysis,
|
|
281
|
+
});
|
|
282
|
+
results.push({ step: "tsc --noEmit", ok: true, output: stdout + stderr });
|
|
283
|
+
}
|
|
284
|
+
catch (err) {
|
|
285
|
+
const anyErr = err;
|
|
286
|
+
results.push({
|
|
287
|
+
step: "tsc --noEmit",
|
|
288
|
+
ok: false,
|
|
289
|
+
output: `${anyErr.stdout ?? ""}\n${anyErr.stderr ?? ""}\n${anyErr.message ?? ""}`.trim() || String(err),
|
|
290
|
+
});
|
|
291
|
+
}
|
|
292
|
+
if (framework === "playwright") {
|
|
293
|
+
try {
|
|
294
|
+
const { stdout, stderr } = await execFileAsync("npx", ["--yes", "playwright", "test", "--list", file], {
|
|
295
|
+
cwd: process.cwd(),
|
|
296
|
+
timeout: ctx.config.timeouts.analysis,
|
|
297
|
+
});
|
|
298
|
+
results.push({ step: "playwright test --list", ok: true, output: stdout + stderr });
|
|
299
|
+
}
|
|
300
|
+
catch (err) {
|
|
301
|
+
const anyErr = err;
|
|
302
|
+
results.push({
|
|
303
|
+
step: "playwright test --list",
|
|
304
|
+
ok: false,
|
|
305
|
+
output: `${anyErr.stdout ?? ""}\n${anyErr.stderr ?? ""}\n${anyErr.message ?? ""}`.trim() || String(err),
|
|
306
|
+
});
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
const ok = results.every((r) => r.ok);
|
|
310
|
+
const text = [
|
|
311
|
+
`## Dry-run: ${ok ? "PASS" : "FAIL"}`,
|
|
312
|
+
...results.flatMap((r) => [
|
|
313
|
+
"",
|
|
314
|
+
`### ${r.step}: ${r.ok ? "✅" : "❌"}`,
|
|
315
|
+
"```",
|
|
316
|
+
r.output.slice(0, 2_000),
|
|
317
|
+
"```",
|
|
318
|
+
]),
|
|
319
|
+
].join("\n");
|
|
320
|
+
return { text, structured: { ok, results, file } };
|
|
321
|
+
},
|
|
322
|
+
},
|
|
323
|
+
{
|
|
324
|
+
name: "tr_generate_assertion",
|
|
325
|
+
capability: "creation",
|
|
326
|
+
title: "Generate a stable assertion",
|
|
327
|
+
description: "TestRelic parallel to Playwright's browser_generate_locator. Given a journey step, returns a stable framework-appropriate assertion the agent can paste into a test.",
|
|
328
|
+
inputSchema: {
|
|
329
|
+
step: z.string().describe("The journey step to assert, e.g. 'payment success banner visible'"),
|
|
330
|
+
framework: z.enum(["playwright", "cypress", "jest", "vitest"]).optional().default("playwright"),
|
|
331
|
+
},
|
|
332
|
+
handler: async (input, ctx) => {
|
|
333
|
+
const step = input.step;
|
|
334
|
+
const framework = input.framework ?? "playwright";
|
|
335
|
+
const prompt = `Return a single-line ${framework} assertion that confirms: "${step}". Prefer role-based selectors. No preamble.`;
|
|
336
|
+
const sampled = await ctx.sampling.createMessage(prompt, { temperature: 0.1, maxTokens: 120 });
|
|
337
|
+
let assertion = sampled.text.trim().split(/\r?\n/)[0] ?? "";
|
|
338
|
+
if (!assertion) {
|
|
339
|
+
assertion =
|
|
340
|
+
framework === "playwright"
|
|
341
|
+
? `await expect(page.getByText(${JSON.stringify(step)})).toBeVisible();`
|
|
342
|
+
: framework === "cypress"
|
|
343
|
+
? `cy.contains(${JSON.stringify(step)}).should("be.visible");`
|
|
344
|
+
: `expect(screen.getByText(${JSON.stringify(step)})).toBeTruthy();`;
|
|
345
|
+
}
|
|
346
|
+
return {
|
|
347
|
+
text: ["```ts", assertion, "```"].join("\n"),
|
|
348
|
+
structured: { framework, assertion, fallback: sampled.fallback },
|
|
349
|
+
};
|
|
350
|
+
},
|
|
351
|
+
},
|
|
352
|
+
];
|
|
353
|
+
export function registerCreationTools(ctx, register) {
|
|
354
|
+
for (const t of creationTools)
|
|
355
|
+
register(t);
|
|
356
|
+
}
|
|
357
|
+
//# sourceMappingURL=index.js.map
|