@kirrosh/zond 0.7.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/CHANGELOG.md +130 -0
- package/LICENSE +21 -0
- package/README.md +130 -0
- package/package.json +53 -0
- package/src/bun-types.d.ts +5 -0
- package/src/cli/commands/add-api.ts +51 -0
- package/src/cli/commands/ai-generate.ts +106 -0
- package/src/cli/commands/chat.ts +43 -0
- package/src/cli/commands/ci-init.ts +163 -0
- package/src/cli/commands/collections.ts +41 -0
- package/src/cli/commands/compare.ts +129 -0
- package/src/cli/commands/coverage.ts +156 -0
- package/src/cli/commands/doctor.ts +127 -0
- package/src/cli/commands/init.ts +84 -0
- package/src/cli/commands/mcp.ts +16 -0
- package/src/cli/commands/run.ts +156 -0
- package/src/cli/commands/runs.ts +108 -0
- package/src/cli/commands/serve.ts +22 -0
- package/src/cli/commands/update.ts +142 -0
- package/src/cli/commands/validate.ts +18 -0
- package/src/cli/index.ts +529 -0
- package/src/cli/output.ts +24 -0
- package/src/cli/runtime.ts +7 -0
- package/src/core/agent/agent-loop.ts +116 -0
- package/src/core/agent/context-manager.ts +41 -0
- package/src/core/agent/system-prompt.ts +28 -0
- package/src/core/agent/tools/diagnose-failure.ts +51 -0
- package/src/core/agent/tools/explore-api.ts +40 -0
- package/src/core/agent/tools/index.ts +46 -0
- package/src/core/agent/tools/query-results.ts +40 -0
- package/src/core/agent/tools/run-tests.ts +38 -0
- package/src/core/agent/tools/send-request.ts +44 -0
- package/src/core/agent/tools/validate-tests.ts +23 -0
- package/src/core/agent/types.ts +22 -0
- package/src/core/diagnostics/failure-hints.ts +63 -0
- package/src/core/generator/ai/ai-generator.ts +61 -0
- package/src/core/generator/ai/llm-client.ts +159 -0
- package/src/core/generator/ai/output-parser.ts +307 -0
- package/src/core/generator/ai/prompt-builder.ts +153 -0
- package/src/core/generator/ai/types.ts +56 -0
- package/src/core/generator/chunker.ts +47 -0
- package/src/core/generator/coverage-scanner.ts +87 -0
- package/src/core/generator/data-factory.ts +115 -0
- package/src/core/generator/endpoint-warnings.ts +43 -0
- package/src/core/generator/index.ts +12 -0
- package/src/core/generator/openapi-reader.ts +143 -0
- package/src/core/generator/schema-utils.ts +52 -0
- package/src/core/generator/serializer.ts +189 -0
- package/src/core/generator/types.ts +48 -0
- package/src/core/parser/filter.ts +14 -0
- package/src/core/parser/index.ts +21 -0
- package/src/core/parser/schema.ts +175 -0
- package/src/core/parser/types.ts +52 -0
- package/src/core/parser/variables.ts +154 -0
- package/src/core/parser/yaml-parser.ts +85 -0
- package/src/core/reporter/console.ts +175 -0
- package/src/core/reporter/index.ts +23 -0
- package/src/core/reporter/json.ts +9 -0
- package/src/core/reporter/junit.ts +78 -0
- package/src/core/reporter/types.ts +12 -0
- package/src/core/runner/assertions.ts +173 -0
- package/src/core/runner/execute-run.ts +97 -0
- package/src/core/runner/executor.ts +183 -0
- package/src/core/runner/http-client.ts +69 -0
- package/src/core/runner/index.ts +12 -0
- package/src/core/runner/types.ts +48 -0
- package/src/core/setup-api.ts +113 -0
- package/src/core/utils.ts +9 -0
- package/src/db/queries.ts +774 -0
- package/src/db/schema.ts +159 -0
- package/src/mcp/descriptions.ts +88 -0
- package/src/mcp/server.ts +52 -0
- package/src/mcp/tools/ci-init.ts +54 -0
- package/src/mcp/tools/coverage-analysis.ts +141 -0
- package/src/mcp/tools/describe-endpoint.ts +241 -0
- package/src/mcp/tools/explore-api.ts +84 -0
- package/src/mcp/tools/generate-and-save.ts +129 -0
- package/src/mcp/tools/generate-missing-tests.ts +91 -0
- package/src/mcp/tools/generate-tests-guide.ts +391 -0
- package/src/mcp/tools/manage-server.ts +86 -0
- package/src/mcp/tools/query-db.ts +255 -0
- package/src/mcp/tools/run-tests.ts +71 -0
- package/src/mcp/tools/save-test-suite.ts +218 -0
- package/src/mcp/tools/send-request.ts +63 -0
- package/src/mcp/tools/set-work-dir.ts +35 -0
- package/src/mcp/tools/setup-api.ts +84 -0
- package/src/mcp/tools/validate-tests.ts +43 -0
- package/src/tui/chat-ui.ts +150 -0
- package/src/web/data/collection-state.ts +360 -0
- package/src/web/routes/api.ts +234 -0
- package/src/web/routes/dashboard.ts +313 -0
- package/src/web/routes/runs.ts +64 -0
- package/src/web/schemas.ts +121 -0
- package/src/web/server.ts +134 -0
- package/src/web/static/htmx.min.js +1 -0
- package/src/web/static/style.css +827 -0
- package/src/web/views/endpoints-tab.ts +170 -0
- package/src/web/views/health-strip.ts +92 -0
- package/src/web/views/layout.ts +48 -0
- package/src/web/views/results.ts +209 -0
- package/src/web/views/runs-tab.ts +126 -0
- package/src/web/views/suites-tab.ts +153 -0
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Suites tab: all YAML files on disk with run status and step details.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import type { CollectionState, SuiteViewState, StepViewState } from "../data/collection-state.ts";
|
|
6
|
+
import { escapeHtml } from "./layout.ts";
|
|
7
|
+
import { basename } from "node:path";
|
|
8
|
+
|
|
9
|
+
export function renderSuitesTab(state: CollectionState): string {
|
|
10
|
+
if (state.suites.length === 0) {
|
|
11
|
+
return `<div class="tab-empty">No test suites found on disk. Generate tests with <code>generate_tests_guide</code> or <code>generate_and_save</code>.</div>`;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
const rows = state.suites.map((s, i) => renderSuiteRow(s, i)).join("");
|
|
15
|
+
return `<div class="suite-list">${rows}</div>`;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function renderSuiteRow(suite: SuiteViewState, index: number): string {
|
|
19
|
+
const detailId = `suite-detail-${index}`;
|
|
20
|
+
|
|
21
|
+
if (suite.status === "parse_error") {
|
|
22
|
+
return `
|
|
23
|
+
<div class="suite-row suite-error-row">
|
|
24
|
+
<div class="suite-info">
|
|
25
|
+
<div class="suite-name">${escapeHtml(basename(suite.filePath || suite.name))}</div>
|
|
26
|
+
<div class="suite-desc" style="color:var(--fail);">${escapeHtml(suite.parseError ?? "Parse error")}</div>
|
|
27
|
+
</div>
|
|
28
|
+
<div class="suite-tags"></div>
|
|
29
|
+
<div class="suite-steps-count">-</div>
|
|
30
|
+
<div class="suite-result fail">error</div>
|
|
31
|
+
</div>`;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const tags = suite.tags.map(t => {
|
|
35
|
+
const tagClass = t === "smoke" ? "smoke" : t === "crud" ? "crud" : t === "auth" ? "auth" : t === "destructive" ? "destructive" : "";
|
|
36
|
+
return `<span class="tag-pill ${tagClass}">${escapeHtml(t)}</span>`;
|
|
37
|
+
}).join("");
|
|
38
|
+
|
|
39
|
+
const total = suite.runResult
|
|
40
|
+
? suite.runResult.passed + suite.runResult.failed + suite.runResult.skipped
|
|
41
|
+
: 0;
|
|
42
|
+
|
|
43
|
+
let resultHtml: string;
|
|
44
|
+
if (suite.status === "passed") {
|
|
45
|
+
resultHtml = `<div class="suite-result pass">${suite.runResult!.passed}/${total} ✓</div>`;
|
|
46
|
+
} else if (suite.status === "failed") {
|
|
47
|
+
resultHtml = `<div class="suite-result fail">${suite.runResult!.passed}/${total} ✗</div>`;
|
|
48
|
+
} else {
|
|
49
|
+
resultHtml = `<div class="suite-result not-run">not run</div>`;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// Step detail rows
|
|
53
|
+
const stepsHtml = suite.steps.length > 0
|
|
54
|
+
? suite.steps.map((step, si) => renderStepRow(step, index, si)).join("")
|
|
55
|
+
: `<div style="font-size:0.75rem;color:var(--text-dim);padding:0.5rem;">No run results yet</div>`;
|
|
56
|
+
|
|
57
|
+
return `
|
|
58
|
+
<div class="suite-row"
|
|
59
|
+
onclick="var d=document.getElementById('${detailId}');d.style.display=d.style.display==='none'?'block':'none'">
|
|
60
|
+
<div class="suite-info">
|
|
61
|
+
<div class="suite-name">${escapeHtml(suite.name)}</div>
|
|
62
|
+
${suite.description ? `<div class="suite-desc">${escapeHtml(suite.description)}</div>` : ""}
|
|
63
|
+
</div>
|
|
64
|
+
<div class="suite-tags">${tags}</div>
|
|
65
|
+
<div class="suite-steps-count">${suite.stepCount} steps</div>
|
|
66
|
+
${resultHtml}
|
|
67
|
+
</div>
|
|
68
|
+
<div class="suite-detail" id="${detailId}" style="display:none">
|
|
69
|
+
${stepsHtml}
|
|
70
|
+
</div>`;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function renderStepRow(step: StepViewState, suiteIdx: number, stepIdx: number): string {
|
|
74
|
+
const icon = step.status === "pass"
|
|
75
|
+
? '<span class="step-icon pass">✓</span>'
|
|
76
|
+
: step.status === "fail" || step.status === "error"
|
|
77
|
+
? '<span class="step-icon fail">✗</span>'
|
|
78
|
+
: '<span class="step-icon skip">○</span>';
|
|
79
|
+
|
|
80
|
+
const labelStyle = step.status === "fail" || step.status === "error"
|
|
81
|
+
? ' style="color:var(--fail);"'
|
|
82
|
+
: step.status === "skip"
|
|
83
|
+
? ' style="color:var(--skip);"'
|
|
84
|
+
: "";
|
|
85
|
+
|
|
86
|
+
const duration = step.durationMs != null
|
|
87
|
+
? `<span class="step-duration">${step.durationMs}ms</span>`
|
|
88
|
+
: `<span class="step-duration">-</span>`;
|
|
89
|
+
|
|
90
|
+
// Captures
|
|
91
|
+
const captureHtml = step.captures && Object.keys(step.captures).length > 0
|
|
92
|
+
? `<span class="step-captures">${Object.entries(step.captures).map(([k, v]) =>
|
|
93
|
+
`<span class="capture-pill">${escapeHtml(k)} = ${escapeHtml(String(v))}</span>`
|
|
94
|
+
).join("")}</span>`
|
|
95
|
+
: `<span class="step-captures"></span>`;
|
|
96
|
+
|
|
97
|
+
const detailId = `s-${suiteIdx}-step-${stepIdx}`;
|
|
98
|
+
const hasDetail = (step.status === "fail" || step.status === "error") &&
|
|
99
|
+
((step.assertions && step.assertions.length > 0) || step.hint || step.responseBody);
|
|
100
|
+
|
|
101
|
+
const clickHandler = hasDetail
|
|
102
|
+
? ` onclick="event.stopPropagation();var d=document.getElementById('${detailId}');d.style.display=d.style.display==='none'?'block':'none'"`
|
|
103
|
+
: "";
|
|
104
|
+
|
|
105
|
+
let detailPanel = "";
|
|
106
|
+
if (hasDetail) {
|
|
107
|
+
let detailContent = "";
|
|
108
|
+
|
|
109
|
+
// Request info
|
|
110
|
+
if (step.requestMethod && step.requestUrl) {
|
|
111
|
+
detailContent += `<div style="font-family:var(--font-mono);font-size:0.75rem;color:var(--text-dim);margin-bottom:0.4rem;">
|
|
112
|
+
${escapeHtml(step.requestMethod)} ${escapeHtml(step.requestUrl)}</div>`;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
// Assertions
|
|
116
|
+
if (step.assertions && step.assertions.length > 0) {
|
|
117
|
+
detailContent += step.assertions.map(a => {
|
|
118
|
+
const aIcon = a.passed
|
|
119
|
+
? '<span class="assertion-icon pass">✓</span>'
|
|
120
|
+
: '<span class="assertion-icon fail">✗</span>';
|
|
121
|
+
const actual = !a.passed && a.actual !== undefined
|
|
122
|
+
? ` <span class="assertion-actual">(got ${escapeHtml(JSON.stringify(a.actual))})</span>` : "";
|
|
123
|
+
return `<div class="assertion-row">${aIcon} <span class="assertion-field">${escapeHtml(a.field)}:</span> <span class="assertion-rule">${escapeHtml(a.rule)}</span>${actual}</div>`;
|
|
124
|
+
}).join("");
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// Error message
|
|
128
|
+
if (step.errorMessage) {
|
|
129
|
+
detailContent += `<div style="font-family:var(--font-mono);font-size:0.75rem;color:var(--fail);margin-top:0.25rem;">${escapeHtml(step.errorMessage)}</div>`;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
// Failure hint
|
|
133
|
+
if (step.hint) {
|
|
134
|
+
detailContent += `<div class="failure-hint"><span>⚠</span> ${escapeHtml(step.hint)}</div>`;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
// Response body toggle
|
|
138
|
+
if (step.responseBody) {
|
|
139
|
+
const truncated = step.responseBody.length > 2000 ? step.responseBody.slice(0, 2000) + "..." : step.responseBody;
|
|
140
|
+
detailContent += `<div class="req-res-toggle" onclick="event.stopPropagation();var b=this.nextElementSibling;b.style.display=b.style.display==='none'?'block':'none'">▼ Response Body</div>
|
|
141
|
+
<div class="req-res-body" style="display:none;"><pre style="font-size:0.7rem;margin:0.25rem 0;">${escapeHtml(truncated)}</pre></div>`;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
detailPanel = `<div class="step-detail-panel" id="${detailId}" style="display:none">${detailContent}</div>`;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
return `<div class="step-row"${clickHandler}>
|
|
148
|
+
${icon}
|
|
149
|
+
<span class="step-label"${labelStyle}>${escapeHtml(step.name)}</span>
|
|
150
|
+
${captureHtml}
|
|
151
|
+
${duration}
|
|
152
|
+
</div>${detailPanel}`;
|
|
153
|
+
}
|