@skyramp/mcp 0.3.1 → 0.3.2-rc.pom-2
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/build/index.js +2 -1
- package/build/prompts/code-reuse.d.ts +7 -1
- package/build/prompts/code-reuse.js +8 -5
- package/build/prompts/code-reuse.test.d.ts +1 -0
- package/build/prompts/code-reuse.test.js +62 -0
- package/build/prompts/pom-aware-code-reuse.d.ts +6 -1
- package/build/prompts/pom-aware-code-reuse.js +100 -53
- package/build/prompts/pom-aware-code-reuse.test.d.ts +1 -0
- package/build/prompts/pom-aware-code-reuse.test.js +11 -0
- package/build/prompts/test-recommendation/diffExecutionPlan.d.ts +4 -2
- package/build/prompts/test-recommendation/diffExecutionPlan.js +11 -65
- package/build/prompts/test-recommendation/fullRepoCatalog.d.ts +2 -2
- package/build/prompts/test-recommendation/recommendationSections.js +5 -2
- package/build/prompts/test-recommendation/scopeAssessment.js +1 -1
- package/build/prompts/test-recommendation/test-recommendation-prompt.d.ts +26 -1
- package/build/prompts/test-recommendation/test-recommendation-prompt.js +68 -56
- package/build/prompts/test-recommendation/test-recommendation-prompt.test.js +48 -11
- package/build/prompts/testbot/testbot-prompts.d.ts +1 -1
- package/build/prompts/testbot/testbot-prompts.js +67 -21
- package/build/prompts/testbot/testbot-prompts.test.js +44 -0
- package/build/recommendation/budgeters/diversityBalancedBudgeter.d.ts +7 -0
- package/build/recommendation/budgeters/diversityBalancedBudgeter.js +71 -0
- package/build/recommendation/budgeters/diversityBalancedBudgeter.test.d.ts +1 -0
- package/build/recommendation/budgeters/diversityBalancedBudgeter.test.js +75 -0
- package/build/recommendation/budgeters/fixedNBudgeter.d.ts +7 -0
- package/build/recommendation/budgeters/fixedNBudgeter.js +11 -0
- package/build/recommendation/budgeters/fixedNBudgeter.test.d.ts +1 -0
- package/build/recommendation/budgeters/fixedNBudgeter.test.js +66 -0
- package/build/recommendation/budgeters/shared.d.ts +19 -0
- package/build/recommendation/budgeters/shared.js +66 -0
- package/build/recommendation/discriminators.d.ts +31 -0
- package/build/recommendation/discriminators.js +355 -0
- package/build/recommendation/discriminators.test.d.ts +1 -0
- package/build/recommendation/discriminators.test.js +324 -0
- package/build/recommendation/diversity.d.ts +47 -0
- package/build/recommendation/diversity.js +101 -0
- package/build/recommendation/diversity.test.d.ts +1 -0
- package/build/recommendation/diversity.test.js +77 -0
- package/build/recommendation/planRanker.d.ts +50 -0
- package/build/recommendation/planRanker.js +67 -0
- package/build/recommendation/planRanker.test.d.ts +1 -0
- package/build/recommendation/planRanker.test.js +110 -0
- package/build/recommendation/testFixtures.d.ts +25 -0
- package/build/recommendation/testFixtures.js +45 -0
- package/build/resources/testbotResource.js +4 -1
- package/build/services/ScenarioGenerationService.d.ts +5 -0
- package/build/services/ScenarioGenerationService.js +16 -1
- package/build/services/ScenarioGenerationService.test.js +44 -0
- package/build/services/TestExecutionService.d.ts +15 -1
- package/build/services/TestExecutionService.js +210 -55
- package/build/services/TestExecutionService.test.js +397 -0
- package/build/services/TestGenerationService.js +19 -1
- package/build/services/TestGenerationService.test.js +58 -0
- package/build/tool-phases.js +1 -0
- package/build/toolNames.d.ts +19 -0
- package/build/toolNames.js +19 -0
- package/build/tools/code-refactor/codeReuseTool.d.ts +7 -0
- package/build/tools/code-refactor/codeReuseTool.js +130 -4
- package/build/tools/code-refactor/codeReuseTool.test.d.ts +1 -0
- package/build/tools/code-refactor/codeReuseTool.test.js +290 -0
- package/build/tools/executeSkyrampTestTool.js +8 -2
- package/build/tools/generate-tests/generateBatchScenarioRestTool.d.ts +6 -1
- package/build/tools/generate-tests/generateBatchScenarioRestTool.js +110 -17
- package/build/tools/generate-tests/generateBatchScenarioRestTool.test.js +147 -0
- package/build/tools/generate-tests/generateContractRestTool.js +11 -1
- package/build/tools/generate-tests/generateIntegrationRestTool.js +24 -1
- package/build/tools/generate-tests/generateIntegrationRestTool.test.d.ts +1 -0
- package/build/tools/generate-tests/generateIntegrationRestTool.test.js +159 -0
- package/build/tools/generate-tests/planGuard.d.ts +13 -0
- package/build/tools/generate-tests/planGuard.js +78 -0
- package/build/tools/generate-tests/planGuard.test.d.ts +1 -0
- package/build/tools/generate-tests/planGuard.test.js +185 -0
- package/build/tools/generate-tests/scenarioFileIdentity.d.ts +10 -0
- package/build/tools/generate-tests/scenarioFileIdentity.js +46 -0
- package/build/tools/generate-tests/scenarioLint.d.ts +30 -0
- package/build/tools/generate-tests/scenarioLint.js +150 -0
- package/build/tools/generate-tests/scenarioLint.test.d.ts +1 -0
- package/build/tools/generate-tests/scenarioLint.test.js +100 -0
- package/build/tools/submitReportTool.js +78 -0
- package/build/tools/submitReportTool.test.js +255 -0
- package/build/tools/test-management/analyzeChangesTool.js +55 -2
- package/build/tools/test-management/analyzeChangesTool.test.js +12 -0
- package/build/tools/test-management/index.d.ts +1 -0
- package/build/tools/test-management/index.js +1 -0
- package/build/tools/test-management/registerTestPlanTool.d.ts +2 -0
- package/build/tools/test-management/registerTestPlanTool.js +329 -0
- package/build/tools/test-management/registerTestPlanTool.test.d.ts +1 -0
- package/build/tools/test-management/registerTestPlanTool.test.js +296 -0
- package/build/types/Recommendation.d.ts +97 -0
- package/build/types/Recommendation.js +48 -0
- package/build/types/RepositoryAnalysis.d.ts +14 -14
- package/build/types/TestExecution.d.ts +2 -0
- package/build/types/TestRecommendation.d.ts +12 -1
- package/build/types/TestRecommendation.js +26 -11
- package/build/types/TestTypes.js +1 -1
- package/build/utils/AnalysisStateManager.d.ts +47 -0
- package/build/utils/docker.test.js +1 -1
- package/build/utils/planMatchKeys.d.ts +61 -0
- package/build/utils/planMatchKeys.js +125 -0
- package/build/utils/pom-scope/import-expansion.d.ts +5 -0
- package/build/utils/pom-scope/import-expansion.js +32 -0
- package/build/utils/pom-scope/index.d.ts +39 -0
- package/build/utils/pom-scope/index.js +120 -0
- package/build/utils/pom-scope/index.test.d.ts +1 -0
- package/build/utils/pom-scope/index.test.js +239 -0
- package/build/utils/pom-scope/pom-files.d.ts +3 -0
- package/build/utils/pom-scope/pom-files.js +48 -0
- package/build/utils/pom-scope/pom-files.test.d.ts +1 -0
- package/build/utils/pom-scope/pom-files.test.js +29 -0
- package/build/utils/pom-scope/scoring.d.ts +20 -0
- package/build/utils/pom-scope/scoring.js +45 -0
- package/build/utils/pom-scope/scoring.test.d.ts +1 -0
- package/build/utils/pom-scope/scoring.test.js +39 -0
- package/build/utils/pom-scope/selector-extractor.d.ts +7 -0
- package/build/utils/pom-scope/selector-extractor.js +57 -0
- package/build/utils/pom-scope/selector-extractor.test.d.ts +1 -0
- package/build/utils/pom-scope/selector-extractor.test.js +67 -0
- package/build/utils/pom-verify/__fixtures__/af-style/asset-list.page.d.ts +5 -0
- package/build/utils/pom-verify/__fixtures__/af-style/asset-list.page.js +5 -0
- package/build/utils/pom-verify/__fixtures__/af-style/pageobjects/asset-list-page.d.ts +5 -0
- package/build/utils/pom-verify/__fixtures__/af-style/pageobjects/asset-list-page.js +9 -0
- package/build/utils/pom-verify/__fixtures__/af-style/report.iframe.page.d.ts +4 -0
- package/build/utils/pom-verify/__fixtures__/af-style/report.iframe.page.js +4 -0
- package/build/utils/pom-verify/__fixtures__/af-style/workflow-footer.page.d.ts +4 -0
- package/build/utils/pom-verify/__fixtures__/af-style/workflow-footer.page.js +4 -0
- package/build/utils/pom-verify/bindings.d.ts +19 -0
- package/build/utils/pom-verify/bindings.js +161 -0
- package/build/utils/pom-verify/bindings.test.d.ts +1 -0
- package/build/utils/pom-verify/bindings.test.js +164 -0
- package/build/utils/pom-verify/calls.d.ts +16 -0
- package/build/utils/pom-verify/calls.js +42 -0
- package/build/utils/pom-verify/calls.test.d.ts +1 -0
- package/build/utils/pom-verify/calls.test.js +61 -0
- package/build/utils/pom-verify/index.d.ts +4 -0
- package/build/utils/pom-verify/index.js +4 -0
- package/build/utils/pom-verify/resolve.d.ts +7 -0
- package/build/utils/pom-verify/resolve.js +27 -0
- package/build/utils/pom-verify/resolve.test.d.ts +1 -0
- package/build/utils/pom-verify/resolve.test.js +68 -0
- package/build/utils/pom-verify/strip.d.ts +9 -0
- package/build/utils/pom-verify/strip.js +89 -0
- package/build/utils/pom-verify/verify.d.ts +14 -0
- package/build/utils/pom-verify/verify.js +158 -0
- package/build/utils/pom-verify/verify.test.d.ts +1 -0
- package/build/utils/pom-verify/verify.test.js +325 -0
- package/build/utils/reportVerification.d.ts +61 -0
- package/build/utils/reportVerification.js +104 -0
- package/build/utils/reportVerification.test.d.ts +1 -0
- package/build/utils/reportVerification.test.js +185 -0
- package/build/utils/scenarioDrafting.js +5 -5
- package/build/utils/versions.d.ts +3 -3
- package/build/utils/versions.js +1 -1
- package/build/utils/workspaceAuth.d.ts +9 -1
- package/build/utils/workspaceAuth.js +25 -5
- package/build/utils/workspaceAuth.test.js +48 -0
- package/build/workspace/workspace.d.ts +20 -0
- package/build/workspace/workspace.js +4 -0
- package/build/workspace/workspace.test.js +10 -0
- package/node_modules/playwright/lib/mcp/skyramp/traceRecordingBackend.js +77 -8
- package/node_modules/playwright/node_modules/playwright-core/lib/generated/injectedScriptSource.js +1 -1
- package/node_modules/playwright/node_modules/playwright-core/lib/generated/pollingRecorderSource.js +1 -1
- package/node_modules/playwright/node_modules/playwright-core/lib/utils/isomorphic/volatileDate.js +101 -0
- package/node_modules/playwright/node_modules/playwright-core/lib/utils.js +2 -0
- package/node_modules/playwright/node_modules/playwright-core/lib/vite/traceViewer/assets/{codeMirrorModule-aszq5EdG.js → codeMirrorModule-Bzd72-bG.js} +1 -1
- package/node_modules/playwright/node_modules/playwright-core/lib/vite/traceViewer/assets/{defaultSettingsView-BxS7Jm4s.js → defaultSettingsView-DzxTioTK.js} +101 -101
- package/node_modules/playwright/node_modules/playwright-core/lib/vite/traceViewer/{index.D4JTTy4R.js → index.BGc30U3S.js} +1 -1
- package/node_modules/playwright/node_modules/playwright-core/lib/vite/traceViewer/index.html +2 -2
- package/node_modules/playwright/node_modules/playwright-core/lib/vite/traceViewer/{uiMode.DaRMQKOI.js → uiMode.IaDrb29A.js} +1 -1
- package/node_modules/playwright/node_modules/playwright-core/lib/vite/traceViewer/uiMode.html +2 -2
- package/node_modules/playwright/node_modules/playwright-core/package.json +1 -1
- package/node_modules/playwright/node_modules/playwright-core/src/generated/injectedScriptSource.ts +1 -1
- package/node_modules/playwright/node_modules/playwright-core/src/generated/pollingRecorderSource.ts +1 -1
- package/node_modules/playwright/node_modules/playwright-core/src/utils/isomorphic/volatileDate.ts +131 -0
- package/node_modules/playwright/node_modules/playwright-core/src/utils.ts +1 -0
- package/node_modules/playwright/package.json +1 -1
- package/package.json +3 -3
- package/node_modules/playwright/node_modules/playwright-core/.DS_Store +0 -0
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
|
+
import { readFile } from "fs/promises";
|
|
3
|
+
import * as path from "path";
|
|
2
4
|
import { logger } from "../../utils/logger.js";
|
|
3
|
-
import { getCodeReusePrompt } from "../../prompts/code-reuse.js";
|
|
5
|
+
import { getCodeReusePrompt, isPomAwareTarget } from "../../prompts/code-reuse.js";
|
|
6
|
+
import { selectScopedPoms } from "../../utils/pom-scope/index.js";
|
|
7
|
+
import { verifyReuse } from "../../utils/pom-verify/index.js";
|
|
4
8
|
import { codeRefactoringSchema, languageSchema, } from "../../types/TestTypes.js";
|
|
5
9
|
import { SKYRAMP_UTILS_HEADER } from "../../utils/utils.js";
|
|
6
10
|
import { AnalyticsService } from "../../services/AnalyticsService.js";
|
|
@@ -14,8 +18,55 @@ const codeReuseSchema = z.object({
|
|
|
14
18
|
modularizeCode: codeRefactoringSchema.shape.modularizeCode,
|
|
15
19
|
prompt: z
|
|
16
20
|
.string()
|
|
21
|
+
.default("")
|
|
17
22
|
.describe("The code content to analyze and optimize for code reuse"),
|
|
23
|
+
verify: z
|
|
24
|
+
.boolean()
|
|
25
|
+
.default(false)
|
|
26
|
+
.describe("Verify a previously written refactored test's POM calls against source instead of returning the reuse prompt"),
|
|
18
27
|
});
|
|
28
|
+
/** Renders a `VerifyResult` (from `verifyReuse`) as the text report returned to the agent. */
|
|
29
|
+
export function formatVerifyReport(r) {
|
|
30
|
+
if (r.ok) {
|
|
31
|
+
const lines = [`VERIFICATION PASSED — ${r.checkedCalls} calls checked.`];
|
|
32
|
+
if (r.unverifiable.length > 0) {
|
|
33
|
+
lines.push(`Unverifiable (informational): ${r.unverifiable.join(", ")}`);
|
|
34
|
+
}
|
|
35
|
+
return lines.join("\n");
|
|
36
|
+
}
|
|
37
|
+
const lines = ["VERIFICATION FAILED"];
|
|
38
|
+
for (const v of r.violations) {
|
|
39
|
+
const location = v.searched.length > 1 ? `${v.searched[0]} (+1-hop)` : v.searched[0];
|
|
40
|
+
lines.push(`${v.binding}.${v.member} — not found in ${location}. Available members: ${v.availableMembers.join(", ")}`);
|
|
41
|
+
}
|
|
42
|
+
if (r.parseError) {
|
|
43
|
+
lines.push(`Parse error (fix the syntax and re-verify): ${r.parseError}`);
|
|
44
|
+
}
|
|
45
|
+
lines.push("Each violation may be remapped AT MOST ONCE, and only to a member listed above. If it is flagged again, or you are not confident, restore the raw inline sequence (from your mapping table or `<testFile>.raw.bak`) with a `// kept inline: no matching POM method` comment. Re-run this verification after every fix pass. Do not finish until it reports PASSED.");
|
|
46
|
+
return lines.join("\n\n");
|
|
47
|
+
}
|
|
48
|
+
/** Renders the zero-reuse compliance-gate failure: a spec with 0 verified POM calls whose raw
|
|
49
|
+
* selectors nonetheless overlap tier-1 POM candidates — i.e. reuse was skipped, not unavailable. */
|
|
50
|
+
export function formatZeroReuseGateFailure(tier1) {
|
|
51
|
+
const lines = [
|
|
52
|
+
"VERIFICATION FAILED — this test contains no page-object reuse, but detection found reusable POM candidates for its selectors:",
|
|
53
|
+
...tier1.map((p) => `- ${p.file} (matched: ${p.matchedTokens.join(", ")})`),
|
|
54
|
+
"Map the matching actions to these files' methods/properties (re-run skyramp_reuse_code for the full workflow if needed). A raw locator that matches a POM property verbatim is evidence TO substitute, never a reason to stay inline. Only finish with raw locators if you add a comment for each file above — `// kept inline: <file basename> — <reason it cannot cover this test>` — then re-run this verification, which will accept the documented files.",
|
|
55
|
+
];
|
|
56
|
+
return lines.join("\n");
|
|
57
|
+
}
|
|
58
|
+
/** True when `specContent` already carries a `// kept inline: <basename> …` comment line naming
|
|
59
|
+
* this candidate's file — the machine-checkable escape for `formatZeroReuseGateFailure`'s
|
|
60
|
+
* documentation instruction (and the same marker the demote flow already writes). */
|
|
61
|
+
function isKeptInlineDocumented(specContent, file) {
|
|
62
|
+
const basename = path.basename(file).replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
63
|
+
// Anchored full-format match: `// kept inline: <basename> — <non-empty reason>`.
|
|
64
|
+
// The basename must start right after the marker (no substring collisions with
|
|
65
|
+
// longer filenames) and a dash-separated reason is required (a bare basename is
|
|
66
|
+
// not documentation — the reason is what makes the escape PR-reviewable).
|
|
67
|
+
const re = new RegExp(`//\\s*kept inline:\\s*${basename}\\s*[—–-]+\\s*\\S`);
|
|
68
|
+
return specContent.split("\n").some((line) => re.test(line));
|
|
69
|
+
}
|
|
19
70
|
const TOOL_NAME = "skyramp_reuse_code";
|
|
20
71
|
export function registerCodeReuseTool(server) {
|
|
21
72
|
server.registerTool(TOOL_NAME, {
|
|
@@ -51,8 +102,8 @@ export function registerCodeReuseTool(server) {
|
|
|
51
102
|
**CRITICAL**: NON TRACE BASED TESTS ARE ALREADY MODULARIZED AND DO NOT NEED CODE REUSE.
|
|
52
103
|
The tool will provide step-by-step instructions that MUST be followed exactly.
|
|
53
104
|
|
|
54
|
-
**POM-AWARE MODE (TypeScript/Playwright
|
|
55
|
-
For projects with \`language: "typescript"\` and \`framework: "playwright"\`, this tool first checks whether the project has an existing Page Object Model (POM) library (\`pageobjects/\` directories or \`*.page.ts\` files). If POMs are found, the test is refactored to use those POM classes and methods — replacing raw inline locators with the existing abstractions. SkyrampUtils consolidation is skipped on this path. If no POMs are found, the tool falls back to the standard SkyrampUtils path. New POM creation is out of scope — existing POMs are reused only. After POM-aware code reuse, do NOT call skyramp_modularization.`,
|
|
105
|
+
**POM-AWARE MODE (TypeScript/JavaScript + Playwright):**
|
|
106
|
+
For projects with \`language: "typescript" or "javascript"\` and \`framework: "playwright"\`, this tool first checks whether the project has an existing Page Object Model (POM) library (\`pageobjects/\` directories or \`*.page.ts\` / \`*.page.js\` files). If POMs are found, the test is refactored to use those POM classes and methods — replacing raw inline locators with the existing abstractions. SkyrampUtils consolidation is skipped on this path. If no POMs are found, the tool falls back to the standard SkyrampUtils path. New POM creation is out of scope — existing POMs are reused only. After POM-aware code reuse, do NOT call skyramp_modularization.`,
|
|
56
107
|
inputSchema: codeReuseSchema.shape,
|
|
57
108
|
_meta: {
|
|
58
109
|
keywords: [
|
|
@@ -66,6 +117,52 @@ export function registerCodeReuseTool(server) {
|
|
|
66
117
|
}, async (params) => {
|
|
67
118
|
let errorResult;
|
|
68
119
|
try {
|
|
120
|
+
if (params.verify) {
|
|
121
|
+
try {
|
|
122
|
+
const r = await verifyReuse(params.testFile, params.language);
|
|
123
|
+
// A spec with 0 checked calls, 0 violations, 0 unverifiable entries, and no parse error
|
|
124
|
+
// still carries all of its raw selectors verbatim, so running detection against it is
|
|
125
|
+
// meaningful — a spec with real bindings (checkedCalls > 0), a deep-chain-only reuse
|
|
126
|
+
// (unverifiable > 0), or broken syntax (parseError) never reaches this branch: those
|
|
127
|
+
// need their own remediation, not the mapping-instructions gate. Scoping errors (or a
|
|
128
|
+
// legitimately POM-less repo, tier1 === []) fall through to the normal PASSED response
|
|
129
|
+
// below: this gate must never turn an infra error into a false compliance failure.
|
|
130
|
+
if (r.checkedCalls === 0 &&
|
|
131
|
+
r.violations.length === 0 &&
|
|
132
|
+
r.unverifiable.length === 0 &&
|
|
133
|
+
!r.parseError &&
|
|
134
|
+
isPomAwareTarget(params.language, params.framework)) {
|
|
135
|
+
try {
|
|
136
|
+
const specContent = await readFile(params.testFile, "utf8");
|
|
137
|
+
const { tier1, diagnostics } = await selectScopedPoms({ testFile: params.testFile, specContent });
|
|
138
|
+
// Only fire on candidates found via conventional POM dirs (globs) — the selector-grep
|
|
139
|
+
// discovery fallback also matches ordinary in-repo frontend source (app components
|
|
140
|
+
// legitimately contain the spec's test-ids by construction), which is not evidence of
|
|
141
|
+
// skipped reuse in a POM-less repo.
|
|
142
|
+
if (diagnostics.discovery === "globs" && tier1.length > 0) {
|
|
143
|
+
const undocumented = tier1.filter((p) => !isKeptInlineDocumented(specContent, p.file));
|
|
144
|
+
if (undocumented.length > 0) {
|
|
145
|
+
return { content: [{ type: "text", text: formatZeroReuseGateFailure(undocumented) }] };
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
catch (err) {
|
|
150
|
+
logger.warning("Zero-reuse compliance gate scoping failed — falling back to normal verify report", { error: String(err) });
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
return { content: [{ type: "text", text: formatVerifyReport(r) }] };
|
|
154
|
+
}
|
|
155
|
+
catch (err) {
|
|
156
|
+
return {
|
|
157
|
+
content: [
|
|
158
|
+
{
|
|
159
|
+
type: "text",
|
|
160
|
+
text: `VERIFICATION ERROR (treat as unverified, do not retry more than once): ${String(err)}`,
|
|
161
|
+
},
|
|
162
|
+
],
|
|
163
|
+
};
|
|
164
|
+
}
|
|
165
|
+
}
|
|
69
166
|
logger.info("Analyzing code for reuse opportunities", {
|
|
70
167
|
testFile: params.testFile,
|
|
71
168
|
language: params.language,
|
|
@@ -73,7 +170,36 @@ export function registerCodeReuseTool(server) {
|
|
|
73
170
|
});
|
|
74
171
|
// Stage so testbot includes the generated files in its output commit.
|
|
75
172
|
await stageGeneratedPaths(params.testFile);
|
|
76
|
-
|
|
173
|
+
// Deterministic POM pre-scoping (TS/JS+Playwright only). Never blocks:
|
|
174
|
+
// any error or empty result falls back to the agent-driven STEP 1.
|
|
175
|
+
let scopedPoms;
|
|
176
|
+
if (isPomAwareTarget(params.language, params.framework)) {
|
|
177
|
+
try {
|
|
178
|
+
const t0 = Date.now();
|
|
179
|
+
const { tier1, tier2, diagnostics } = await selectScopedPoms({
|
|
180
|
+
testFile: params.testFile,
|
|
181
|
+
specContent: params.prompt,
|
|
182
|
+
});
|
|
183
|
+
if (tier1.length + tier2.length > 0) {
|
|
184
|
+
scopedPoms = { tier1, tier2 };
|
|
185
|
+
}
|
|
186
|
+
else if (diagnostics.selectors > 0 && diagnostics.discovery !== "skipped-too-large") {
|
|
187
|
+
// A clean scan ran (glob or selector-grep) and found zero overlap — distinct from
|
|
188
|
+
// "scoping wasn't attempted" (no selectors) or "scan was skipped" (repo too large).
|
|
189
|
+
scopedPoms = { tier1: [], tier2: [], scannedNoOverlap: true };
|
|
190
|
+
}
|
|
191
|
+
logger.info("POM pre-scoping", {
|
|
192
|
+
...diagnostics,
|
|
193
|
+
tier1: tier1.length,
|
|
194
|
+
tier2: tier2.length,
|
|
195
|
+
ms: Date.now() - t0,
|
|
196
|
+
});
|
|
197
|
+
}
|
|
198
|
+
catch (err) {
|
|
199
|
+
logger.warning("POM pre-scoping failed — falling back to agent-driven detection", { error: String(err) });
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
const codeReusePrompt = getCodeReusePrompt(params.testFile, params.language, params.framework, scopedPoms);
|
|
77
203
|
return {
|
|
78
204
|
content: [
|
|
79
205
|
{
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,290 @@
|
|
|
1
|
+
// @ts-nocheck - Jest ESM type inference issues
|
|
2
|
+
import { jest } from "@jest/globals";
|
|
3
|
+
import * as fs from "fs";
|
|
4
|
+
import * as os from "os";
|
|
5
|
+
import * as path from "path";
|
|
6
|
+
jest.unstable_mockModule("../../utils/logger.js", () => ({
|
|
7
|
+
logger: { info: jest.fn(), error: jest.fn(), warning: jest.fn(), debug: jest.fn() },
|
|
8
|
+
}));
|
|
9
|
+
jest.unstable_mockModule("../../services/AnalyticsService.js", () => ({
|
|
10
|
+
AnalyticsService: { pushMCPToolEvent: jest.fn().mockResolvedValue(undefined) },
|
|
11
|
+
}));
|
|
12
|
+
jest.unstable_mockModule("../../utils/gitStaging.js", () => ({
|
|
13
|
+
stageGeneratedPaths: jest.fn().mockResolvedValue(undefined),
|
|
14
|
+
}));
|
|
15
|
+
jest.unstable_mockModule("../../utils/pom-verify/index.js", () => ({
|
|
16
|
+
verifyReuse: jest.fn().mockResolvedValue({ ok: true, violations: [], unverifiable: [], checkedCalls: 3 }),
|
|
17
|
+
}));
|
|
18
|
+
const { registerCodeReuseTool, formatVerifyReport, formatZeroReuseGateFailure } = await import("./codeReuseTool.js");
|
|
19
|
+
const { stageGeneratedPaths } = await import("../../utils/gitStaging.js");
|
|
20
|
+
const { verifyReuse } = await import("../../utils/pom-verify/index.js");
|
|
21
|
+
function captureToolHandler() {
|
|
22
|
+
let handler;
|
|
23
|
+
registerCodeReuseTool({ registerTool: (_n, _o, fn) => { handler = fn; } });
|
|
24
|
+
return handler;
|
|
25
|
+
}
|
|
26
|
+
describe("skyramp_reuse_code — verify mode", () => {
|
|
27
|
+
it("returns the verification report and skips prompt generation/staging when verify:true", async () => {
|
|
28
|
+
const handler = captureToolHandler();
|
|
29
|
+
const result = await handler({
|
|
30
|
+
testFile: "t.spec.ts",
|
|
31
|
+
language: "typescript",
|
|
32
|
+
framework: "playwright",
|
|
33
|
+
modularizeCode: false,
|
|
34
|
+
prompt: "",
|
|
35
|
+
verify: true,
|
|
36
|
+
});
|
|
37
|
+
expect(result.content[0].text).toBe("VERIFICATION PASSED — 3 calls checked.");
|
|
38
|
+
expect(verifyReuse).toHaveBeenCalledWith("t.spec.ts", "typescript");
|
|
39
|
+
expect(stageGeneratedPaths).not.toHaveBeenCalled();
|
|
40
|
+
});
|
|
41
|
+
});
|
|
42
|
+
describe("skyramp_reuse_code — verify mode zero-reuse compliance gate", () => {
|
|
43
|
+
it("fails verification when a zero-binding spec's selectors match a fixture POM file", async () => {
|
|
44
|
+
verifyReuse.mockResolvedValueOnce({ ok: true, violations: [], unverifiable: [], checkedCalls: 0 });
|
|
45
|
+
const dir = fs.mkdtempSync(path.join(os.tmpdir(), "code-reuse-gate-"));
|
|
46
|
+
try {
|
|
47
|
+
fs.writeFileSync(path.join(dir, "package.json"), "{}");
|
|
48
|
+
const pages = path.join(dir, "pages");
|
|
49
|
+
fs.mkdirSync(pages);
|
|
50
|
+
fs.writeFileSync(path.join(pages, "loginPage.js"), `export class LoginPage { constructor(page){ this.signIn = page.locator('[data-test="login-sign-in"]'); } }\n`);
|
|
51
|
+
const testsDir = path.join(dir, "tests");
|
|
52
|
+
fs.mkdirSync(testsDir);
|
|
53
|
+
const testFile = path.join(testsDir, "generated.spec.js");
|
|
54
|
+
fs.writeFileSync(testFile, `await page.locator('[data-test="login-sign-in"]').click();\n`);
|
|
55
|
+
const handler = captureToolHandler();
|
|
56
|
+
const result = await handler({
|
|
57
|
+
testFile,
|
|
58
|
+
language: "javascript",
|
|
59
|
+
framework: "playwright",
|
|
60
|
+
modularizeCode: false,
|
|
61
|
+
prompt: "",
|
|
62
|
+
verify: true,
|
|
63
|
+
});
|
|
64
|
+
expect(result.content[0].text).toContain("VERIFICATION FAILED — this test contains no page-object reuse, but detection found reusable POM candidates for its selectors:");
|
|
65
|
+
expect(result.content[0].text).toContain("loginPage.js");
|
|
66
|
+
}
|
|
67
|
+
finally {
|
|
68
|
+
fs.rmSync(dir, { recursive: true, force: true });
|
|
69
|
+
}
|
|
70
|
+
});
|
|
71
|
+
it("returns the normal PASSED response when a zero-binding spec's selectors only match discovery-fallback app source (no conventional POM dirs)", async () => {
|
|
72
|
+
// A POM-less repo whose in-repo frontend legitimately contains the spec's test-ids (built the
|
|
73
|
+
// markup, not a page-object layer) must not be gated — the selector-grep discovery fallback
|
|
74
|
+
// finding overlap here is not evidence that reuse was skipped.
|
|
75
|
+
verifyReuse.mockResolvedValueOnce({ ok: true, violations: [], unverifiable: [], checkedCalls: 0 });
|
|
76
|
+
const dir = fs.mkdtempSync(path.join(os.tmpdir(), "code-reuse-gate-nomatch-"));
|
|
77
|
+
try {
|
|
78
|
+
fs.writeFileSync(path.join(dir, "package.json"), "{}");
|
|
79
|
+
fs.mkdirSync(path.join(dir, "src", "components"), { recursive: true });
|
|
80
|
+
fs.writeFileSync(path.join(dir, "src", "components", "LoginForm.jsx"), `export function LoginForm() {\n return (<form>\n <input data-test="login-email" />\n <input data-test="login-password" />\n <button data-test="login-sign-in">Sign in</button>\n </form>);\n}\n`);
|
|
81
|
+
const testsDir = path.join(dir, "tests");
|
|
82
|
+
fs.mkdirSync(testsDir);
|
|
83
|
+
const testFile = path.join(testsDir, "generated.spec.js");
|
|
84
|
+
fs.writeFileSync(testFile, `await page.locator('[data-test="login-email"]').fill('a@b.c');\nawait page.locator('[data-test="login-password"]').fill('x');\nawait page.locator('[data-test="login-sign-in"]').click();\n`);
|
|
85
|
+
const handler = captureToolHandler();
|
|
86
|
+
const result = await handler({
|
|
87
|
+
testFile,
|
|
88
|
+
language: "javascript",
|
|
89
|
+
framework: "playwright",
|
|
90
|
+
modularizeCode: false,
|
|
91
|
+
prompt: "",
|
|
92
|
+
verify: true,
|
|
93
|
+
});
|
|
94
|
+
expect(result.content[0].text).toBe("VERIFICATION PASSED — 0 calls checked.");
|
|
95
|
+
}
|
|
96
|
+
finally {
|
|
97
|
+
fs.rmSync(dir, { recursive: true, force: true });
|
|
98
|
+
}
|
|
99
|
+
});
|
|
100
|
+
it("does not fire the gate when a deep-chain-only reuse leaves unverifiable entries (0 checked calls is not 0 reuse)", async () => {
|
|
101
|
+
// Reuse expressed entirely as a 2-hop chain (assetPage.section.method()) is non-strict, so
|
|
102
|
+
// verifyReuse reports it under `unverifiable`, not `checkedCalls`. Without the unverifiable
|
|
103
|
+
// guard this would look identical to a zero-reuse spec and falsely fire the gate.
|
|
104
|
+
verifyReuse.mockResolvedValueOnce({
|
|
105
|
+
ok: true,
|
|
106
|
+
violations: [],
|
|
107
|
+
unverifiable: ["assetPage.section.method"],
|
|
108
|
+
checkedCalls: 0,
|
|
109
|
+
});
|
|
110
|
+
const dir = fs.mkdtempSync(path.join(os.tmpdir(), "code-reuse-gate-deepchain-"));
|
|
111
|
+
try {
|
|
112
|
+
fs.writeFileSync(path.join(dir, "package.json"), "{}");
|
|
113
|
+
const pages = path.join(dir, "pages");
|
|
114
|
+
fs.mkdirSync(pages);
|
|
115
|
+
fs.writeFileSync(path.join(pages, "assetPage.js"), `export class AssetPage { constructor(page){ this.entryBtn = page.locator('[data-test="asset-entry"]'); } }\n`);
|
|
116
|
+
const testsDir = path.join(dir, "tests");
|
|
117
|
+
fs.mkdirSync(testsDir);
|
|
118
|
+
const testFile = path.join(testsDir, "refactored.spec.js");
|
|
119
|
+
fs.writeFileSync(testFile, `await assetPage.section.method();\nawait page.locator('[data-test="asset-entry"]').click();\n`);
|
|
120
|
+
const handler = captureToolHandler();
|
|
121
|
+
const result = await handler({
|
|
122
|
+
testFile,
|
|
123
|
+
language: "javascript",
|
|
124
|
+
framework: "playwright",
|
|
125
|
+
modularizeCode: false,
|
|
126
|
+
prompt: "",
|
|
127
|
+
verify: true,
|
|
128
|
+
});
|
|
129
|
+
expect(result.content[0].text).toBe("VERIFICATION PASSED — 0 calls checked.\nUnverifiable (informational): assetPage.section.method");
|
|
130
|
+
}
|
|
131
|
+
finally {
|
|
132
|
+
fs.rmSync(dir, { recursive: true, force: true });
|
|
133
|
+
}
|
|
134
|
+
});
|
|
135
|
+
it("falls through to PASSED when every tier1 candidate is documented with a `// kept inline:` comment", async () => {
|
|
136
|
+
verifyReuse.mockResolvedValueOnce({ ok: true, violations: [], unverifiable: [], checkedCalls: 0 });
|
|
137
|
+
const dir = fs.mkdtempSync(path.join(os.tmpdir(), "code-reuse-gate-documented-"));
|
|
138
|
+
try {
|
|
139
|
+
fs.writeFileSync(path.join(dir, "package.json"), "{}");
|
|
140
|
+
const pages = path.join(dir, "pages");
|
|
141
|
+
fs.mkdirSync(pages);
|
|
142
|
+
fs.writeFileSync(path.join(pages, "loginPage.js"), `export class LoginPage { constructor(page){ this.signIn = page.locator('[data-test="login-sign-in"]'); } }\n`);
|
|
143
|
+
const testsDir = path.join(dir, "tests");
|
|
144
|
+
fs.mkdirSync(testsDir);
|
|
145
|
+
const testFile = path.join(testsDir, "generated.spec.js");
|
|
146
|
+
fs.writeFileSync(testFile, `// kept inline: loginPage.js — this flow doesn't own the sign-in frame\nawait page.locator('[data-test="login-sign-in"]').click();\n`);
|
|
147
|
+
const handler = captureToolHandler();
|
|
148
|
+
const result = await handler({
|
|
149
|
+
testFile,
|
|
150
|
+
language: "javascript",
|
|
151
|
+
framework: "playwright",
|
|
152
|
+
modularizeCode: false,
|
|
153
|
+
prompt: "",
|
|
154
|
+
verify: true,
|
|
155
|
+
});
|
|
156
|
+
expect(result.content[0].text).toBe("VERIFICATION PASSED — 0 calls checked.");
|
|
157
|
+
}
|
|
158
|
+
finally {
|
|
159
|
+
fs.rmSync(dir, { recursive: true, force: true });
|
|
160
|
+
}
|
|
161
|
+
});
|
|
162
|
+
it("does NOT accept a bare-basename kept-inline comment (a reason is required)", async () => {
|
|
163
|
+
verifyReuse.mockResolvedValueOnce({ ok: true, violations: [], unverifiable: [], checkedCalls: 0 });
|
|
164
|
+
const dir = fs.mkdtempSync(path.join(os.tmpdir(), "code-reuse-gate-bare-"));
|
|
165
|
+
try {
|
|
166
|
+
fs.writeFileSync(path.join(dir, "package.json"), "{}");
|
|
167
|
+
const pages = path.join(dir, "pages");
|
|
168
|
+
fs.mkdirSync(pages);
|
|
169
|
+
fs.writeFileSync(path.join(pages, "loginPage.js"), `export class LoginPage { constructor(page){ this.signIn = page.locator('[data-test="login-sign-in"]'); } }\n`);
|
|
170
|
+
const testsDir = path.join(dir, "tests");
|
|
171
|
+
fs.mkdirSync(testsDir);
|
|
172
|
+
const testFile = path.join(testsDir, "generated.spec.js");
|
|
173
|
+
fs.writeFileSync(testFile,
|
|
174
|
+
// bare basename, no dash-separated reason — the lazy bypass the format check rejects
|
|
175
|
+
`// kept inline: loginPage.js\nawait page.locator('[data-test="login-sign-in"]').click();\n`);
|
|
176
|
+
const handler = captureToolHandler();
|
|
177
|
+
const result = await handler({
|
|
178
|
+
testFile,
|
|
179
|
+
language: "javascript",
|
|
180
|
+
framework: "playwright",
|
|
181
|
+
modularizeCode: false,
|
|
182
|
+
prompt: "",
|
|
183
|
+
verify: true,
|
|
184
|
+
});
|
|
185
|
+
expect(result.content[0].text).toContain("VERIFICATION FAILED — this test contains no page-object reuse");
|
|
186
|
+
}
|
|
187
|
+
finally {
|
|
188
|
+
fs.rmSync(dir, { recursive: true, force: true });
|
|
189
|
+
}
|
|
190
|
+
});
|
|
191
|
+
it("surfaces the parse-error remediation, not the gate text, when the spec has broken syntax", async () => {
|
|
192
|
+
verifyReuse.mockResolvedValueOnce({
|
|
193
|
+
ok: false,
|
|
194
|
+
violations: [],
|
|
195
|
+
unverifiable: [],
|
|
196
|
+
checkedCalls: 0,
|
|
197
|
+
parseError: "SyntaxError: Unexpected end of input",
|
|
198
|
+
});
|
|
199
|
+
const dir = fs.mkdtempSync(path.join(os.tmpdir(), "code-reuse-gate-parseerror-"));
|
|
200
|
+
try {
|
|
201
|
+
fs.writeFileSync(path.join(dir, "package.json"), "{}");
|
|
202
|
+
const pages = path.join(dir, "pages");
|
|
203
|
+
fs.mkdirSync(pages);
|
|
204
|
+
fs.writeFileSync(path.join(pages, "loginPage.js"), `export class LoginPage { constructor(page){ this.signIn = page.locator('[data-test="login-sign-in"]'); } }\n`);
|
|
205
|
+
const testsDir = path.join(dir, "tests");
|
|
206
|
+
fs.mkdirSync(testsDir);
|
|
207
|
+
const testFile = path.join(testsDir, "generated.spec.js");
|
|
208
|
+
fs.writeFileSync(testFile, `await page.locator('[data-test="login-sign-in"]').click(\n`); // unbalanced paren
|
|
209
|
+
const handler = captureToolHandler();
|
|
210
|
+
const result = await handler({
|
|
211
|
+
testFile,
|
|
212
|
+
language: "javascript",
|
|
213
|
+
framework: "playwright",
|
|
214
|
+
modularizeCode: false,
|
|
215
|
+
prompt: "",
|
|
216
|
+
verify: true,
|
|
217
|
+
});
|
|
218
|
+
expect(result.content[0].text).toContain("Parse error (fix the syntax and re-verify): SyntaxError: Unexpected end of input");
|
|
219
|
+
expect(result.content[0].text).not.toContain("VERIFICATION FAILED — this test contains no page-object reuse");
|
|
220
|
+
}
|
|
221
|
+
finally {
|
|
222
|
+
fs.rmSync(dir, { recursive: true, force: true });
|
|
223
|
+
}
|
|
224
|
+
});
|
|
225
|
+
it("never fires the gate for a non-Playwright framework, even with matching POM candidates", async () => {
|
|
226
|
+
verifyReuse.mockResolvedValueOnce({ ok: true, violations: [], unverifiable: [], checkedCalls: 0 });
|
|
227
|
+
const dir = fs.mkdtempSync(path.join(os.tmpdir(), "code-reuse-gate-nonplaywright-"));
|
|
228
|
+
try {
|
|
229
|
+
fs.writeFileSync(path.join(dir, "package.json"), "{}");
|
|
230
|
+
const pages = path.join(dir, "pages");
|
|
231
|
+
fs.mkdirSync(pages);
|
|
232
|
+
fs.writeFileSync(path.join(pages, "loginPage.js"), `export class LoginPage { constructor(page){ this.signIn = page.locator('[data-test="login-sign-in"]'); } }\n`);
|
|
233
|
+
const testsDir = path.join(dir, "tests");
|
|
234
|
+
fs.mkdirSync(testsDir);
|
|
235
|
+
const testFile = path.join(testsDir, "generated.spec.js");
|
|
236
|
+
fs.writeFileSync(testFile, `await page.locator('[data-test="login-sign-in"]').click();\n`);
|
|
237
|
+
const handler = captureToolHandler();
|
|
238
|
+
const result = await handler({
|
|
239
|
+
testFile,
|
|
240
|
+
language: "javascript",
|
|
241
|
+
framework: "cypress",
|
|
242
|
+
modularizeCode: false,
|
|
243
|
+
prompt: "",
|
|
244
|
+
verify: true,
|
|
245
|
+
});
|
|
246
|
+
expect(result.content[0].text).toBe("VERIFICATION PASSED — 0 calls checked.");
|
|
247
|
+
}
|
|
248
|
+
finally {
|
|
249
|
+
fs.rmSync(dir, { recursive: true, force: true });
|
|
250
|
+
}
|
|
251
|
+
});
|
|
252
|
+
it("falls through to PASSED when POM scoping fails (e.g. testFile in a nonexistent directory)", async () => {
|
|
253
|
+
verifyReuse.mockResolvedValueOnce({ ok: true, violations: [], unverifiable: [], checkedCalls: 0 });
|
|
254
|
+
const testFile = path.join(os.tmpdir(), "code-reuse-gate-does-not-exist", "generated.spec.js");
|
|
255
|
+
const handler = captureToolHandler();
|
|
256
|
+
const result = await handler({
|
|
257
|
+
testFile,
|
|
258
|
+
language: "javascript",
|
|
259
|
+
framework: "playwright",
|
|
260
|
+
modularizeCode: false,
|
|
261
|
+
prompt: "",
|
|
262
|
+
verify: true,
|
|
263
|
+
});
|
|
264
|
+
expect(result.content[0].text).toBe("VERIFICATION PASSED — 0 calls checked.");
|
|
265
|
+
});
|
|
266
|
+
});
|
|
267
|
+
describe("formatVerifyReport", () => {
|
|
268
|
+
it("reports a violation block with the fix-rule paragraph, not the passed message", () => {
|
|
269
|
+
const text = formatVerifyReport({
|
|
270
|
+
ok: false,
|
|
271
|
+
violations: [{ binding: "pm", member: "fakeMethod", searched: ["a.ts", "b.ts"], availableMembers: ["realMethod"] }],
|
|
272
|
+
unverifiable: [],
|
|
273
|
+
checkedCalls: 1,
|
|
274
|
+
});
|
|
275
|
+
expect(text).toContain("VERIFICATION FAILED");
|
|
276
|
+
expect(text).toContain("pm.fakeMethod — not found in a.ts (+1-hop). Available members: realMethod");
|
|
277
|
+
expect(text).toContain("Do not finish until it reports PASSED.");
|
|
278
|
+
});
|
|
279
|
+
});
|
|
280
|
+
describe("formatZeroReuseGateFailure", () => {
|
|
281
|
+
it("lists each tier1 candidate with its matched tokens", () => {
|
|
282
|
+
const text = formatZeroReuseGateFailure([
|
|
283
|
+
{ file: "pages/loginPage.js", score: 1, rareHits: 1, matchedTokens: ["login-sign-in", "login-submit"] },
|
|
284
|
+
]);
|
|
285
|
+
expect(text).toContain("VERIFICATION FAILED — this test contains no page-object reuse, but detection found reusable POM candidates for its selectors:");
|
|
286
|
+
expect(text).toContain("- pages/loginPage.js (matched: login-sign-in, login-submit)");
|
|
287
|
+
expect(text).toContain("A raw locator that matches a POM property verbatim is evidence TO substitute, never a reason to stay inline.");
|
|
288
|
+
expect(text).toContain("Only finish with raw locators if you add a comment for each file above — `// kept inline: <file basename> — <reason it cannot cover this test>` — then re-run this verification, which will accept the documented files.");
|
|
289
|
+
});
|
|
290
|
+
});
|
|
@@ -7,7 +7,7 @@ import { makeProgressReporter } from "../utils/progress.js";
|
|
|
7
7
|
import { TestExecutionStatus } from "../types/TestExecution.js";
|
|
8
8
|
import { getWorkspaceBaseUrl } from "../utils/workspaceAuth.js";
|
|
9
9
|
import { ProgrammingLanguage, TestType } from "../types/TestTypes.js";
|
|
10
|
-
import { StateManager } from "../utils/AnalysisStateManager.js";
|
|
10
|
+
import { StateManager, getTestsRepoDir } from "../utils/AnalysisStateManager.js";
|
|
11
11
|
import { TestSource } from "../types/TestAnalysis.js";
|
|
12
12
|
import { logger } from "../utils/logger.js";
|
|
13
13
|
import { toolError } from "../utils/utils.js";
|
|
@@ -108,10 +108,15 @@ export function registerExecuteSkyrampTestTool(server) {
|
|
|
108
108
|
}
|
|
109
109
|
// Send initial progress
|
|
110
110
|
await sendProgress(0, 100, "Starting test execution...");
|
|
111
|
+
// Cross-repo run (SKYR-3819): tests delivered under the run's testsRepoDir
|
|
112
|
+
// (set by skyramp_analyze_changes, run-scoped) live outside workspacePath.
|
|
113
|
+
// Thread it through so the executor mounts the test repo and service
|
|
114
|
+
// matching still resolves the SUT baseUrl/dockerNetwork.
|
|
115
|
+
const testRepoPath = getTestsRepoDir();
|
|
111
116
|
// Always resolve workspace config for dockerNetwork (host networking)
|
|
112
117
|
// and optionally inject SKYRAMP_TEST_BASE_URL if not already set.
|
|
113
118
|
if (params.workspacePath) {
|
|
114
|
-
const { baseUrl, dockerNetwork, candidates } = await getWorkspaceBaseUrl(params.workspacePath, params.testFile, params.language);
|
|
119
|
+
const { baseUrl, dockerNetwork, candidates } = await getWorkspaceBaseUrl(params.workspacePath, params.testFile, params.language, testRepoPath);
|
|
115
120
|
useHostNetwork = !!dockerNetwork;
|
|
116
121
|
if (!process.env.SKYRAMP_TEST_BASE_URL) {
|
|
117
122
|
if (baseUrl) {
|
|
@@ -142,6 +147,7 @@ export function registerExecuteSkyrampTestTool(server) {
|
|
|
142
147
|
const result = await executionService.executeTest({
|
|
143
148
|
testFile: params.testFile,
|
|
144
149
|
workspacePath: params.workspacePath,
|
|
150
|
+
testRepoPath,
|
|
145
151
|
language: params.language,
|
|
146
152
|
testType: params.testType,
|
|
147
153
|
token: effectiveToken,
|
|
@@ -6,9 +6,10 @@ export declare const stepSchema: z.ZodEffects<z.ZodObject<{
|
|
|
6
6
|
path: z.ZodString;
|
|
7
7
|
requestBody: z.ZodEffects<z.ZodOptional<z.ZodString>, string | undefined, string | undefined>;
|
|
8
8
|
queryParams: z.ZodEffects<z.ZodOptional<z.ZodString>, string | undefined, string | undefined>;
|
|
9
|
-
responseBody: z.ZodOptional<z.ZodString>;
|
|
9
|
+
responseBody: z.ZodEffects<z.ZodOptional<z.ZodString>, string | undefined, string | undefined>;
|
|
10
10
|
statusCode: z.ZodOptional<z.ZodNumber>;
|
|
11
11
|
responseHeaders: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodArray<z.ZodString, "many">>>;
|
|
12
|
+
uniqueFields: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
|
|
12
13
|
}, "strip", z.ZodTypeAny, {
|
|
13
14
|
path: string;
|
|
14
15
|
method: HttpMethod;
|
|
@@ -17,6 +18,7 @@ export declare const stepSchema: z.ZodEffects<z.ZodObject<{
|
|
|
17
18
|
requestBody?: string | undefined;
|
|
18
19
|
responseBody?: string | undefined;
|
|
19
20
|
responseHeaders?: Record<string, string[]> | undefined;
|
|
21
|
+
uniqueFields?: string[] | undefined;
|
|
20
22
|
}, {
|
|
21
23
|
path: string;
|
|
22
24
|
method: HttpMethod;
|
|
@@ -25,6 +27,7 @@ export declare const stepSchema: z.ZodEffects<z.ZodObject<{
|
|
|
25
27
|
requestBody?: string | undefined;
|
|
26
28
|
responseBody?: string | undefined;
|
|
27
29
|
responseHeaders?: Record<string, string[]> | undefined;
|
|
30
|
+
uniqueFields?: string[] | undefined;
|
|
28
31
|
}>, {
|
|
29
32
|
path: string;
|
|
30
33
|
method: HttpMethod;
|
|
@@ -33,6 +36,7 @@ export declare const stepSchema: z.ZodEffects<z.ZodObject<{
|
|
|
33
36
|
requestBody?: string | undefined;
|
|
34
37
|
responseBody?: string | undefined;
|
|
35
38
|
responseHeaders?: Record<string, string[]> | undefined;
|
|
39
|
+
uniqueFields?: string[] | undefined;
|
|
36
40
|
}, {
|
|
37
41
|
path: string;
|
|
38
42
|
method: HttpMethod;
|
|
@@ -41,5 +45,6 @@ export declare const stepSchema: z.ZodEffects<z.ZodObject<{
|
|
|
41
45
|
requestBody?: string | undefined;
|
|
42
46
|
responseBody?: string | undefined;
|
|
43
47
|
responseHeaders?: Record<string, string[]> | undefined;
|
|
48
|
+
uniqueFields?: string[] | undefined;
|
|
44
49
|
}>;
|
|
45
50
|
export declare function registerBatchScenarioTestTool(server: McpServer): void;
|