@skyramp/mcp 0.4.2 → 0.4.3-rc.1
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/execution/wrapperConfig.d.ts +4 -0
- package/build/execution/wrapperConfig.js +14 -2
- package/build/prompts/code-reuse.d.ts +5 -1
- package/build/prompts/code-reuse.js +16 -10
- package/build/prompts/modularization/ui-test-modularization.js +9 -2
- package/build/prompts/pom-aware-code-reuse.d.ts +4 -1
- package/build/prompts/pom-aware-code-reuse.js +38 -6
- package/build/prompts/reuse-hand-off.d.ts +4 -0
- package/build/prompts/reuse-hand-off.js +10 -5
- package/build/prompts/shared-helper-policy.d.ts +5 -0
- package/build/prompts/shared-helper-policy.js +5 -0
- package/build/prompts/test-maintenance/driftAnalysisShared.js +2 -0
- package/build/prompts/test-maintenance/uiDriftAnalysisSections.js +2 -1
- package/build/prompts/testbot/testbot-prompts.js +33 -16
- package/build/recommendation/fakeVerifyContext.d.ts +3 -0
- package/build/recommendation/fakeVerifyContext.js +16 -0
- package/build/recommendation/runVerifiers.js +2 -0
- package/build/recommendation/types.d.ts +12 -0
- package/build/recommendation/verifierContracts.d.ts +24 -2
- package/build/recommendation/verifierContracts.js +32 -4
- package/build/recommendation/verifiers/changedLinesClaimed.d.ts +17 -0
- package/build/recommendation/verifiers/changedLinesClaimed.js +88 -0
- package/build/recommendation/verifiers/expectedValueSourced.js +8 -0
- package/build/recommendation/verifiers/requirementSourced.js +141 -11
- package/build/services/TestGenerationService.js +4 -0
- package/build/tools/code-refactor/caller-gate.d.ts +105 -0
- package/build/tools/code-refactor/caller-gate.js +174 -0
- package/build/tools/code-refactor/changed-helper-report.d.ts +62 -0
- package/build/tools/code-refactor/changed-helper-report.js +114 -0
- package/build/tools/code-refactor/codeReuseTool.js +131 -62
- package/build/tools/code-refactor/enhanceAssertionsTool.js +2 -2
- package/build/tools/code-refactor/helper-callers.d.ts +65 -0
- package/build/tools/code-refactor/helper-callers.js +85 -0
- package/build/tools/code-refactor/modularizationTool.js +10 -0
- package/build/tools/code-refactor/pom-pass-state.d.ts +51 -0
- package/build/tools/code-refactor/pom-pass-state.js +115 -0
- package/build/tools/code-refactor/retrofit-state.d.ts +3 -2
- package/build/tools/code-refactor/retrofit-state.js +11 -12
- package/build/tools/code-refactor/reuse-outcome.d.ts +6 -0
- package/build/tools/code-refactor/reuse-record-store.d.ts +52 -0
- package/build/tools/code-refactor/reuse-record-store.js +126 -0
- package/build/tools/code-refactor/reuse-state.d.ts +18 -6
- package/build/tools/code-refactor/reuse-state.js +78 -131
- package/build/tools/code-refactor/utils-verify-gates.js +3 -3
- package/build/tools/code-refactor/verify-gates.d.ts +4 -0
- package/build/tools/code-refactor/verify-gates.js +8 -1
- package/build/tools/generate-tests/generateContractRestTool.js +7 -7
- package/build/tools/generate-tests/generateIntegrationRestTool.js +8 -8
- package/build/tools/generate-tests/generateUIRestTool.js +3 -3
- package/build/tools/submitReportTool.js +91 -29
- package/build/tools/test-management/actionsTool.js +7 -0
- package/build/tools/test-management/registerTestPlanTool.d.ts +1 -0
- package/build/tools/test-management/registerTestPlanTool.js +70 -22
- package/build/types/RepositoryAnalysis.d.ts +20 -20
- package/build/types/ReuseOutcome.d.ts +96 -0
- package/build/types/TestExecution.d.ts +11 -0
- package/build/types/TestExecution.js +19 -0
- package/build/types/index.d.ts +1 -1
- package/build/utils/branchDiff.d.ts +1 -1
- package/build/utils/branchDiff.js +1 -1
- package/build/utils/changedRuns.d.ts +13 -0
- package/build/utils/changedRuns.js +56 -0
- package/build/utils/featureFlags.d.ts +8 -10
- package/build/utils/featureFlags.js +23 -21
- package/build/utils/normalizeSkyrampImports.d.ts +4 -1
- package/build/utils/normalizeSkyrampImports.js +24 -17
- package/build/utils/pom-scope/ownership.d.ts +19 -0
- package/build/utils/pom-scope/ownership.js +31 -0
- package/build/utils/pom-scope/pom-files.d.ts +8 -0
- package/build/utils/pom-scope/pom-files.js +8 -0
- package/build/utils/pom-scope/scoring.js +13 -1
- package/build/utils/pom-scope/strip.d.ts +10 -0
- package/build/utils/pom-scope/strip.js +9 -0
- package/build/utils/pom-verify/verify.js +13 -2
- package/build/utils/reuseRouting.d.ts +30 -2
- package/build/utils/reuseRouting.js +36 -15
- package/build/utils/testDependencyPolicy.js +4 -16
- package/build/utils/utils-verify/action-sites.d.ts +2 -2
- package/build/utils/utils-verify/body-reach.d.ts +2 -2
- package/build/utils/utils-verify/body-reach.js +4 -1
- package/build/utils/utils-verify/call-sites.d.ts +16 -6
- package/build/utils/utils-verify/call-sites.js +12 -6
- package/build/utils/utils-verify/changed-helpers.d.ts +38 -0
- package/build/utils/utils-verify/changed-helpers.js +91 -0
- package/build/utils/utils-verify/head.d.ts +48 -0
- package/build/utils/utils-verify/head.js +115 -0
- package/build/utils/utils-verify/importers.d.ts +59 -10
- package/build/utils/utils-verify/importers.js +121 -31
- package/build/utils/utils-verify/in-house.js +26 -31
- package/build/utils/utils-verify/index.d.ts +3 -0
- package/build/utils/utils-verify/index.js +3 -0
- package/build/utils/utils-verify/language-spec.js +10 -2
- package/build/utils/utils-verify/locate.d.ts +3 -0
- package/build/utils/utils-verify/locate.js +6 -6
- package/build/utils/utils-verify/parse.d.ts +13 -1
- package/build/utils/utils-verify/parse.js +122 -49
- package/build/utils/utils-verify/retrofit-equivalence.d.ts +0 -5
- package/build/utils/utils-verify/retrofit-equivalence.js +2 -34
- package/build/utils/utils-verify/stage.js +4 -16
- package/build/utils/utils-verify/verify.d.ts +9 -3
- package/build/utils/utils-verify/verify.js +26 -12
- package/node_modules/playwright/lib/dom-analyzer/blueprint.js +1 -0
- package/node_modules/playwright/lib/dom-analyzer/dynamicId.js +1 -0
- package/node_modules/playwright/lib/dom-analyzer/dynamicId.test.js +6 -0
- package/node_modules/playwright/lib/mcp/skyramp/traceRecordingBackend.js +303 -150
- package/node_modules/playwright/node_modules/playwright-core/lib/generated/pollingRecorderSource.js +1 -1
- package/node_modules/playwright/node_modules/playwright-core/lib/vite/traceViewer/assets/{codeMirrorModule-LNgEKtdV.js → codeMirrorModule-Bou8kKzE.js} +1 -1
- package/node_modules/playwright/node_modules/playwright-core/lib/vite/traceViewer/assets/{defaultSettingsView-Bwr1eMKC.js → defaultSettingsView-DTp2-8Si.js} +3 -3
- package/node_modules/playwright/node_modules/playwright-core/lib/vite/traceViewer/{index.BAkLd5DX.js → index.ZYuEFHsi.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.BPopbasy.js → uiMode.GLqsx5cI.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/pollingRecorderSource.ts +1 -1
- package/node_modules/playwright/package.json +1 -1
- package/package.json +2 -2
- package/plugin/prompts/code-reuse/hand-off.md +13 -1
- package/plugin/prompts/plan-tests.md +2 -2
- package/plugin/skills/enhance-assertions/reference/shared-rules.md +1 -0
|
@@ -4,11 +4,13 @@ import * as path from "path";
|
|
|
4
4
|
import { logger } from "../../utils/logger.js";
|
|
5
5
|
import { toolError } from "../../utils/utils.js";
|
|
6
6
|
import { getCodeReusePrompt } from "../../prompts/code-reuse.js";
|
|
7
|
-
import {
|
|
7
|
+
import { reuseRouteFor, isPomFirstTarget, } from "../../utils/reuseRouting.js";
|
|
8
|
+
import { pageObjectCandidates } from "../../utils/pom-scope/ownership.js";
|
|
8
9
|
import { selectScopedPoms, } from "../../utils/pom-scope/index.js";
|
|
9
10
|
import { verifyReuse } from "../../utils/pom-verify/index.js";
|
|
10
11
|
import { infraGateFailure, zeroReuseGateFailure, composeVerifyText, } from "./verify-gates.js";
|
|
11
12
|
import { recordCandidates, recordNoPomLayer, recordVerifyOutcome, recordUtilsReuseIssued, recordUtilsVerifyOutcome, recordUtilsVerifyError, pendingModularization, utilsBaselineFor, } from "./reuse-state.js";
|
|
13
|
+
import { pomPassOutcome, recordPomPassFinished } from "./pom-pass-state.js";
|
|
12
14
|
import { formatUtilsVerifyText } from "./utils-verify-gates.js";
|
|
13
15
|
import { utilsSpecFor, verifyUtils } from "../../utils/utils-verify/index.js";
|
|
14
16
|
import { codeRefactoringSchema, languageSchema, TestType, } from "../../types/TestTypes.js";
|
|
@@ -57,7 +59,7 @@ const codeReuseSchema = z.object({
|
|
|
57
59
|
.describe("Helpers the search read and did not call, one entry each, with the reason."),
|
|
58
60
|
})
|
|
59
61
|
.optional()
|
|
60
|
-
.describe("Verify mode, UI tests only (the SkyrampUtils path
|
|
62
|
+
.describe("Verify mode, UI tests only (the SkyrampUtils path, which follows the page-object pass when page-object reuse is on): what the search for the repository's own browser helper modules read and declined. These are DECLARATIONS — the report records them as what the run states it read, labelled as such, not as a measurement. Pass them on the `verify: true` call once the reuse edits are written."),
|
|
61
63
|
});
|
|
62
64
|
const TOOL_NAME = "skyramp_reuse_code";
|
|
63
65
|
/**
|
|
@@ -103,12 +105,23 @@ const DISCOVER_FIRST_NOTICE = `VERIFICATION NOT RUN — this test file has not b
|
|
|
103
105
|
Discovery is per test file: it scopes page-object candidates to this file's own selectors, so discovery done for another spec does not cover this one. Verification confirms only that the page-object calls a spec ALREADY contains resolve against their source — it cannot name the calls the spec should have made. That is why verifying first reports a spec that reuses almost nothing as PASSED.
|
|
104
106
|
|
|
105
107
|
Follow the result below for this test file; it states what to do next.`;
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
108
|
+
/** The page-object paragraph of the tool description — ONE text per configuration,
|
|
109
|
+
* read at register time like the flag that gates it (nothing here is decided at
|
|
110
|
+
* import). Only advertised with SKYRAMP_FEATURE_POM_REUSE=1: with the flag off this
|
|
111
|
+
* tool never takes the POM path, so describing it would promise behavior it won't do.
|
|
112
|
+
* With shared-helper reuse on as well, a UI test takes both passes, and the text says
|
|
113
|
+
* that instead of a rule and an exception that contradict each other. */
|
|
114
|
+
function pomAwareModeDescription() {
|
|
115
|
+
if (!isPomReuseEnabled())
|
|
116
|
+
return "";
|
|
117
|
+
const head = `
|
|
109
118
|
|
|
110
119
|
**POM-AWARE MODE (TypeScript/JavaScript + Playwright):**
|
|
111
|
-
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.
|
|
120
|
+
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. New POM creation is out of scope — existing POMs are reused only.`;
|
|
121
|
+
return isUtilsReuseEnabled()
|
|
122
|
+
? `${head} For a UI test (\`testType: "ui"\`) this is the FIRST of two passes: once its verification reports PASSED — or it answers "No reusable POM layer detected" — the generation result's next steps call skyramp_modularization and then this tool again, which returns the SkyrampUtils workflow for the steps the POM pass left inline, and whose verify: true call checks both the POM calls and the shared helpers. For any other test type the POM pass is the only pass: do NOT call skyramp_modularization after it. SkyrampUtils consolidation happens only in that second pass, never during the POM pass.`
|
|
123
|
+
: `${head} SkyrampUtils consolidation is skipped on this path. If no POM layer is found, the tool says so and the test is left as generated. After POM-aware code reuse, do NOT call skyramp_modularization — on this path that supersedes WORKFLOW SUMMARY step 6.`;
|
|
124
|
+
}
|
|
112
125
|
/** Whether a `verify: true` call must be turned into the discovery pass it skipped.
|
|
113
126
|
*
|
|
114
127
|
* The two modes return very different things — discovery returns the whole refactoring
|
|
@@ -119,11 +132,26 @@ const POM_AWARE_MODE_DESCRIPTION = `
|
|
|
119
132
|
* `isPomAwareTarget` also carries the feature flag and the browser-test-type check, so a
|
|
120
133
|
* call that could never have reached the POM procedure is never refused for lacking it.
|
|
121
134
|
* Enforced only inside a run — see `markKeyFor`. */
|
|
122
|
-
function mustDiscoverFirst(
|
|
135
|
+
function mustDiscoverFirst(markKey, route) {
|
|
123
136
|
return (markKey !== undefined &&
|
|
124
|
-
|
|
137
|
+
route.pass === "pom" &&
|
|
125
138
|
!discoveredSpecs.has(markKey));
|
|
126
139
|
}
|
|
140
|
+
/** The route for this call, resolved ONCE and handed to every branch, so discovery,
|
|
141
|
+
* verify, the prompt and the modularization check cannot disagree. A page-object
|
|
142
|
+
* target takes the page-object pass — except with both reuse settings on once that
|
|
143
|
+
* pass has finished for the spec: from then on the spec is in the SkyrampUtils flow,
|
|
144
|
+
* which runs over what the page-object pass left inline. */
|
|
145
|
+
async function resolveReuseRoute(params) {
|
|
146
|
+
const finished = isPomFirstTarget(params.language, params.testType) &&
|
|
147
|
+
(await pomPassOutcome(params.testFile)) !== undefined;
|
|
148
|
+
return reuseRouteFor(params.language, params.framework, params.testType, finished);
|
|
149
|
+
}
|
|
150
|
+
function verifyToolResult(v) {
|
|
151
|
+
return v.error
|
|
152
|
+
? toolError(v.text)
|
|
153
|
+
: { content: [{ type: "text", text: v.text }] };
|
|
154
|
+
}
|
|
127
155
|
/** Verify mode for the SkyrampUtils path: stage the shared utils file(s) this spec
|
|
128
156
|
* wrote or imports — located by header, in code, after the edits exist — then check
|
|
129
157
|
* their invariants and record the verdict that clears the utils debt.
|
|
@@ -134,23 +162,15 @@ function mustDiscoverFirst(params, markKey) {
|
|
|
134
162
|
async function runUtilsVerify(params) {
|
|
135
163
|
if (!isUtilsReuseEnabled()) {
|
|
136
164
|
return {
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
type: "text",
|
|
140
|
-
text: "VERIFICATION SKIPPED — SkyrampUtils code reuse is disabled (SKYRAMP_FEATURE_UTILS_REUSE). Nothing to verify; continue.",
|
|
141
|
-
},
|
|
142
|
-
],
|
|
165
|
+
text: "VERIFICATION SKIPPED — SkyrampUtils code reuse is disabled (SKYRAMP_FEATURE_UTILS_REUSE). Nothing to verify; continue.",
|
|
166
|
+
passed: true,
|
|
143
167
|
};
|
|
144
168
|
}
|
|
145
169
|
if (!utilsSpecFor(params.language)) {
|
|
146
170
|
// Say so rather than record a NoUtilsFile verdict nothing established.
|
|
147
171
|
return {
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
type: "text",
|
|
151
|
-
text: `VERIFICATION SKIPPED — shared-helper verification does not cover ${params.language}; nothing was staged or checked. Continue.`,
|
|
152
|
-
},
|
|
153
|
-
],
|
|
172
|
+
text: `VERIFICATION SKIPPED — shared-helper verification does not cover ${params.language}; nothing was staged or checked. Continue.`,
|
|
173
|
+
passed: true,
|
|
154
174
|
};
|
|
155
175
|
}
|
|
156
176
|
try {
|
|
@@ -168,18 +188,14 @@ async function runUtilsVerify(params) {
|
|
|
168
188
|
r.ok = false;
|
|
169
189
|
const recorded = await recordUtilsVerifyOutcome(params.testFile, r, params.language, params.framework, params.testType, undefined, params.inHouse);
|
|
170
190
|
return {
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
recorded,
|
|
180
|
-
}),
|
|
181
|
-
},
|
|
182
|
-
],
|
|
191
|
+
text: formatUtilsVerifyText(r, {
|
|
192
|
+
staged: staging.staged,
|
|
193
|
+
// A refused in-house module is the customer's file, not ours to move.
|
|
194
|
+
unstaged: staging.failed.filter((f) => !staging.inHouse.includes(f)),
|
|
195
|
+
unstagedInHouse: staging.failed.filter((f) => staging.inHouse.includes(f)),
|
|
196
|
+
recorded,
|
|
197
|
+
}),
|
|
198
|
+
passed: r.ok,
|
|
183
199
|
};
|
|
184
200
|
}
|
|
185
201
|
catch (err) {
|
|
@@ -188,30 +204,28 @@ async function runUtilsVerify(params) {
|
|
|
188
204
|
error: String(err),
|
|
189
205
|
});
|
|
190
206
|
await recordUtilsVerifyError(params.testFile);
|
|
191
|
-
return
|
|
192
|
-
`
|
|
207
|
+
return {
|
|
208
|
+
text: `VERIFICATION ERROR — the shared-helper verifier could not run: ${String(err)}. This is not something to retry blindly; ` +
|
|
209
|
+
`it does not block skyramp_enhance_assertions or skyramp_execute_test. If the cause is in your files (unreadable utils file), fix it and verify once more.`,
|
|
210
|
+
passed: false,
|
|
211
|
+
error: true,
|
|
212
|
+
};
|
|
193
213
|
}
|
|
194
214
|
}
|
|
195
|
-
/**
|
|
196
|
-
async function
|
|
197
|
-
// Route on the same predicate the prompt path routes on. A target the POM path
|
|
198
|
-
// never took must not reach the POM verifier — it is the sole writer of the POM
|
|
199
|
-
// reuse summary, and a POM-less run would otherwise emit POM numbers — so every
|
|
200
|
-
// non-POM target, including browser tests with the POM flag off, takes the
|
|
201
|
-
// SkyrampUtils verifier, which is what the prompt path handed it.
|
|
202
|
-
if (!isPomAwareTarget(params.language, params.framework, params.testType)) {
|
|
203
|
-
return runUtilsVerify(params);
|
|
204
|
-
}
|
|
215
|
+
/** The page-object verifier and its gates, recording what the pass established. */
|
|
216
|
+
async function runPomVerify(params, composed) {
|
|
205
217
|
try {
|
|
206
218
|
const r = await verifyReuse(params.testFile, params.language);
|
|
207
|
-
const
|
|
208
|
-
|
|
219
|
+
const target = { ...params, composed };
|
|
220
|
+
const gate = (await infraGateFailure(target, r)) ??
|
|
221
|
+
(await zeroReuseGateFailure(target, r));
|
|
209
222
|
// Record what this pass established before returning the text report:
|
|
210
223
|
// every number in it is already in hand here, so the agent is never
|
|
211
224
|
// asked to read one back out and retype it later.
|
|
212
225
|
await recordVerifyOutcome(params.testFile, r, gate !== undefined, params.language);
|
|
213
226
|
return {
|
|
214
|
-
|
|
227
|
+
text: composeVerifyText(gate, r),
|
|
228
|
+
passed: gate === undefined && r.ok,
|
|
215
229
|
};
|
|
216
230
|
}
|
|
217
231
|
catch (err) {
|
|
@@ -219,14 +233,55 @@ async function runVerify(params) {
|
|
|
219
233
|
testFile: params.testFile,
|
|
220
234
|
error: String(err),
|
|
221
235
|
});
|
|
222
|
-
return
|
|
236
|
+
return {
|
|
237
|
+
text: `VERIFICATION ERROR (treat as unverified, do not retry more than once): ${String(err)}`,
|
|
238
|
+
passed: false,
|
|
239
|
+
error: true,
|
|
240
|
+
};
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
/** Verify mode: check the page-object calls the delivered spec already contains. */
|
|
244
|
+
async function runVerify(params, route) {
|
|
245
|
+
// Route on the same decision the prompt path routes on. A call outside the POM
|
|
246
|
+
// pass must not reach the POM verifier alone — it is the sole writer of the POM
|
|
247
|
+
// reuse summary, and a POM-less run would otherwise emit POM numbers — so every
|
|
248
|
+
// other call, including browser tests with the POM flag off, takes the
|
|
249
|
+
// SkyrampUtils verifier, which is what the prompt path handed it.
|
|
250
|
+
if (route.pass === "utils") {
|
|
251
|
+
return verifyToolResult(route.composed
|
|
252
|
+
? await runComposedVerify(params)
|
|
253
|
+
: await runUtilsVerify(params));
|
|
223
254
|
}
|
|
255
|
+
const pom = await runPomVerify(params, route.composed);
|
|
256
|
+
// Both settings on: a PASSED page-object verify finishes that pass, and the next
|
|
257
|
+
// reuse call on the spec serves the SkyrampUtils flow over what it left inline.
|
|
258
|
+
if (pom.passed && route.composed)
|
|
259
|
+
await recordPomPassFinished(params.testFile);
|
|
260
|
+
return verifyToolResult(pom);
|
|
261
|
+
}
|
|
262
|
+
/** Both reuse settings on, after the page-object pass: one `verify: true` call checks
|
|
263
|
+
* both halves of a spec that calls page objects AND shared helpers. The shared-helper
|
|
264
|
+
* verifier writes the utils record; the page-object verifier — still the only writer
|
|
265
|
+
* of the page-object record — re-checks the page-object calls the helper pass may
|
|
266
|
+
* have moved. Skipped when the page-object pass found no layer: there is nothing to
|
|
267
|
+
* re-check, and a verdict here would publish page-object numbers for a repository
|
|
268
|
+
* without page objects. */
|
|
269
|
+
async function runComposedVerify(params) {
|
|
270
|
+
const utils = await runUtilsVerify(params);
|
|
271
|
+
if ((await pomPassOutcome(params.testFile)) !== "finished")
|
|
272
|
+
return utils;
|
|
273
|
+
const pom = await runPomVerify(params, true);
|
|
274
|
+
return {
|
|
275
|
+
text: `## Page-object calls (the page-object pass)\n\n${pom.text}\n\n## Shared helpers\n\n${utils.text}\n\nThis call checks both. Re-run it after every fix; finish only when both parts report PASSED.`,
|
|
276
|
+
passed: utils.passed && pom.passed,
|
|
277
|
+
...(utils.error || pom.error ? { error: true } : {}),
|
|
278
|
+
};
|
|
224
279
|
}
|
|
225
280
|
/** Discovery mode: the POM-aware refactoring procedure, scoped to this one spec.
|
|
226
281
|
*
|
|
227
282
|
* Both the ordinary discovery call and the refused-verify path go through here, so the
|
|
228
283
|
* workflow a refused verify receives cannot drift from the one a first call receives. */
|
|
229
|
-
async function runDiscovery(params, markKey) {
|
|
284
|
+
async function runDiscovery(params, markKey, route) {
|
|
230
285
|
logger.info("Analyzing code for reuse opportunities", {
|
|
231
286
|
testFile: params.testFile,
|
|
232
287
|
language: params.language,
|
|
@@ -237,7 +292,7 @@ async function runDiscovery(params, markKey) {
|
|
|
237
292
|
// Deterministic POM pre-scoping (TS/JS+Playwright browser tests only).
|
|
238
293
|
// Never blocks: any error or empty result falls back to the agent-driven STEP 1.
|
|
239
294
|
let scopedPoms;
|
|
240
|
-
const pomAware =
|
|
295
|
+
const pomAware = route.pass === "pom";
|
|
241
296
|
// The SkyrampUtils path records its debt here, where the instructions are issued:
|
|
242
297
|
// from this point the tools that follow reuse on this spec refuse until a
|
|
243
298
|
// `verify: true` pass has staged and checked the utils file (SKYR-4220).
|
|
@@ -253,10 +308,19 @@ async function runDiscovery(params, markKey) {
|
|
|
253
308
|
if (pomAware) {
|
|
254
309
|
try {
|
|
255
310
|
const t0 = Date.now();
|
|
256
|
-
const
|
|
311
|
+
const scoped = await selectScopedPoms({
|
|
257
312
|
testFile: params.testFile,
|
|
258
313
|
specContent: params.prompt,
|
|
259
314
|
});
|
|
315
|
+
const { diagnostics } = scoped;
|
|
316
|
+
// Both settings on: the page-object path owns class modules only; a
|
|
317
|
+
// free-function helper module is the in-house path's (pom-scope/ownership.ts).
|
|
318
|
+
const tier1 = route.composed
|
|
319
|
+
? await pageObjectCandidates(scoped.tier1)
|
|
320
|
+
: scoped.tier1;
|
|
321
|
+
const tier2 = route.composed
|
|
322
|
+
? await pageObjectCandidates(scoped.tier2)
|
|
323
|
+
: scoped.tier2;
|
|
260
324
|
if (tier1.length + tier2.length > 0) {
|
|
261
325
|
scopedPoms = { tier1, tier2 };
|
|
262
326
|
// Only report candidates found through the conventional POM globs.
|
|
@@ -276,12 +340,17 @@ async function runDiscovery(params, markKey) {
|
|
|
276
340
|
await recordCandidates(params.testFile, tier1.length, params.language);
|
|
277
341
|
}
|
|
278
342
|
}
|
|
279
|
-
else if (diagnostics.selectors > 0 &&
|
|
280
|
-
diagnostics.discovery !== "skipped-too-large")
|
|
343
|
+
else if ((diagnostics.selectors > 0 &&
|
|
344
|
+
diagnostics.discovery !== "skipped-too-large") ||
|
|
345
|
+
// Every candidate was a free-function module, which is not this path's.
|
|
346
|
+
scoped.tier1.length + scoped.tier2.length > 0) {
|
|
281
347
|
// A clean scan ran (glob or selector-grep) and found zero overlap — distinct from
|
|
282
348
|
// "scoping wasn't attempted" (no selectors) or "scan was skipped" (repo too large).
|
|
283
349
|
scopedPoms = { tier1: [], tier2: [], scannedNoOverlap: true };
|
|
284
350
|
await recordNoPomLayer(params.testFile);
|
|
351
|
+
// Both settings on: no page-object layer finishes that pass at once.
|
|
352
|
+
if (route.composed)
|
|
353
|
+
await recordPomPassFinished(params.testFile);
|
|
285
354
|
}
|
|
286
355
|
logger.info("POM pre-scoping", {
|
|
287
356
|
...diagnostics,
|
|
@@ -294,7 +363,7 @@ async function runDiscovery(params, markKey) {
|
|
|
294
363
|
logger.warning("POM pre-scoping failed — falling back to agent-driven detection", { error: String(err) });
|
|
295
364
|
}
|
|
296
365
|
}
|
|
297
|
-
const codeReusePrompt = getCodeReusePrompt(params.testFile, params.language, params.framework, scopedPoms, params.testType);
|
|
366
|
+
const codeReusePrompt = getCodeReusePrompt(params.testFile, params.language, params.framework, scopedPoms, params.testType, route);
|
|
298
367
|
// Marked on the last line before the return, so the mark cannot be observed until the
|
|
299
368
|
// workflow it stands for has been built. An earlier mark let a verify arriving mid-scope
|
|
300
369
|
// check a spec that had not been handed the procedure yet.
|
|
@@ -336,7 +405,7 @@ export function registerCodeReuseTool(server) {
|
|
|
336
405
|
- No actual function definitions found in other test files
|
|
337
406
|
|
|
338
407
|
Applicability is decided by the caller: when a generation result was produced with \`codeReuse: true\`, it directs you here — ALWAYS follow through (a no-op result is a normal outcome, never a failure). Multi-step tests (UI, E2E, INTEGRATION, LOAD) are the ones that benefit; simple single-request tests (smoke, contract, fuzz) do not request code reuse.
|
|
339
|
-
The tool will provide step-by-step instructions that MUST be followed exactly.${
|
|
408
|
+
The tool will provide step-by-step instructions that MUST be followed exactly.${pomAwareModeDescription()}`,
|
|
340
409
|
inputSchema: codeReuseSchema.shape,
|
|
341
410
|
_meta: {
|
|
342
411
|
keywords: [
|
|
@@ -350,11 +419,12 @@ export function registerCodeReuseTool(server) {
|
|
|
350
419
|
}, async (params) => {
|
|
351
420
|
let errorResult;
|
|
352
421
|
try {
|
|
422
|
+
const route = await resolveReuseRoute(params);
|
|
353
423
|
if (params.verify) {
|
|
354
424
|
const markKey = markKeyFor(params.testFile);
|
|
355
|
-
if (mustDiscoverFirst(
|
|
425
|
+
if (mustDiscoverFirst(markKey, route)) {
|
|
356
426
|
logger.info("Verify called before the discovery pass — returning the discovery workflow", { testFile: params.testFile });
|
|
357
|
-
const workflow = await runDiscovery(params, markKey);
|
|
427
|
+
const workflow = await runDiscovery(params, markKey, route);
|
|
358
428
|
return {
|
|
359
429
|
content: [
|
|
360
430
|
{
|
|
@@ -364,13 +434,12 @@ export function registerCodeReuseTool(server) {
|
|
|
364
434
|
],
|
|
365
435
|
};
|
|
366
436
|
}
|
|
367
|
-
return await runVerify(params);
|
|
437
|
+
return await runVerify(params, route);
|
|
368
438
|
}
|
|
369
439
|
// Modularize-first targets owe skyramp_modularization before reuse is issued —
|
|
370
440
|
// the other half of the pair the hand-off names. Only ever owed for a file
|
|
371
|
-
// generation handed off, so NL callers and
|
|
372
|
-
if (isUtilsReuseEnabled() &&
|
|
373
|
-
!isPomAwareTarget(params.language, params.framework, params.testType)) {
|
|
441
|
+
// generation handed off, so NL callers and the page-object pass are untouched.
|
|
442
|
+
if (isUtilsReuseEnabled() && route.pass === "utils") {
|
|
374
443
|
const owed = await pendingModularization(params.testFile);
|
|
375
444
|
if (owed) {
|
|
376
445
|
errorResult = toolError(owed);
|
|
@@ -381,7 +450,7 @@ export function registerCodeReuseTool(server) {
|
|
|
381
450
|
content: [
|
|
382
451
|
{
|
|
383
452
|
type: "text",
|
|
384
|
-
text: await runDiscovery(params, markKeyFor(params.testFile)),
|
|
453
|
+
text: await runDiscovery(params, markKeyFor(params.testFile), route),
|
|
385
454
|
},
|
|
386
455
|
],
|
|
387
456
|
};
|
|
@@ -12,7 +12,7 @@ function buildAutoApplyInstructions(testFile, testType, enhanceType) {
|
|
|
12
12
|
`Test type: ${testType} | Context: ${enhanceType}`,
|
|
13
13
|
``,
|
|
14
14
|
`Read the file, apply the type-specific assertion guidance below, and write the file back directly.`,
|
|
15
|
-
`Add assertions after each send_request/sendRequest status-code assertion.`,
|
|
15
|
+
`Add assertions after each send_request/sendRequest status-code assertion, except on a setup request.`,
|
|
16
16
|
`Use SDK helper: Python \`skyramp.get_response_value(response, "json.path")\`, JS \`getValue(response, "json.path")\`.`,
|
|
17
17
|
`Do NOT restructure, reformat, add comments, or change imports. Only add assertion lines.`,
|
|
18
18
|
``,
|
|
@@ -99,7 +99,7 @@ export function registerEnhanceAssertionsTool(server) {
|
|
|
99
99
|
`Enhance response body assertions in: \`${testFile}\``,
|
|
100
100
|
`Test type: ${testType} | Context: ${enhanceType}`,
|
|
101
101
|
``,
|
|
102
|
-
`Read the file, add assertions after each send_request/sendRequest status-code assertion, write it back.`,
|
|
102
|
+
`Read the file, add assertions after each send_request/sendRequest status-code assertion, except on a setup request, write it back.`,
|
|
103
103
|
`Use SDK helper: Python \`skyramp.get_response_value(response, "json.path")\`, JS \`getValue(response, "json.path")\`.`,
|
|
104
104
|
``,
|
|
105
105
|
`Shared assertion rules (apply all that fit):`,
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import type { HelperChangeKind } from "../../types/ReuseOutcome.js";
|
|
2
|
+
/**
|
|
3
|
+
* A helper this run changed under the callers it already had — the shared-helper
|
|
4
|
+
* blast radius nothing in the product measured.
|
|
5
|
+
*
|
|
6
|
+
* A shared helper's change reaches every caller at once. The run executes what it
|
|
7
|
+
* DELIVERS, and an untouched caller is not delivered, so a break in one reached the
|
|
8
|
+
* customer's own continuous integration instead of the report. Worse for a browser
|
|
9
|
+
* helper: a changed selector alters what every caller does and appears in no calling
|
|
10
|
+
* test's diff.
|
|
11
|
+
*
|
|
12
|
+
* TWO PREDICATES, never one. `change` is the claim that the helper differs from its
|
|
13
|
+
* incumbent, and rests only on reading the module at HEAD. `callers` present is the
|
|
14
|
+
* separate claim that the caller list can be believed, and rests on the importer walk
|
|
15
|
+
* having read the module's directory. The gate acts on POSITIVE evidence — a file
|
|
16
|
+
* that was read, that imports this module, and that names this helper — and never on
|
|
17
|
+
* the absence of one; the disclosure states the list only when it is present, and
|
|
18
|
+
* says the callers are unknown otherwise. One predicate driving both would make a
|
|
19
|
+
* read error either demand a run for a helper nobody calls or assert a caller set
|
|
20
|
+
* that is wrong.
|
|
21
|
+
*/
|
|
22
|
+
export interface ChangedHelperView {
|
|
23
|
+
/** Absolute path of the shared module. */
|
|
24
|
+
utilsFile: string;
|
|
25
|
+
helper: string;
|
|
26
|
+
change: HelperChangeKind;
|
|
27
|
+
/** ABSOLUTE PATHS of the Skyramp-generated tests that IMPORT this helper — a
|
|
28
|
+
* superset of those that call it, since an imported binding need not be used.
|
|
29
|
+
* Detection stops at the import on purpose: a call can reach the helper through an
|
|
30
|
+
* alias, a re-export, or a local wrapper, and a syntactic call check would miss
|
|
31
|
+
* those. A missed caller is an unexecuted test that breaks in the customer's
|
|
32
|
+
* continuous integration; an unused import costs one extra execution of a test
|
|
33
|
+
* that is runnable by construction. This over-runs rather than under-runs.
|
|
34
|
+
*
|
|
35
|
+
* ABSENT when the importer walk did not run — the one case where a list would be
|
|
36
|
+
* an artefact rather than a lower bound. Present, it is a lower bound: the walk
|
|
37
|
+
* covers ONE directory, so a generated test importing this module from anywhere
|
|
38
|
+
* else is not here. An empty list is the positive statement that the walk ran and
|
|
39
|
+
* found none beside the module. */
|
|
40
|
+
callers?: string[];
|
|
41
|
+
}
|
|
42
|
+
/** A shared module of one directory as this run left it, with what it was. */
|
|
43
|
+
export interface ChangedModule {
|
|
44
|
+
/** Absolute path. A module that is gone is named by the path it had. */
|
|
45
|
+
file: string;
|
|
46
|
+
/** The module at HEAD — read once by the caller, which also used it to decide the
|
|
47
|
+
* file IS a module (the header it carried there). */
|
|
48
|
+
head: string;
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* The helpers this run changed in `modules`, each with the tests beside it that
|
|
52
|
+
* import it.
|
|
53
|
+
*
|
|
54
|
+
* PER DIRECTORY, not per spec. The importer walk reads the modules' directory and
|
|
55
|
+
* nothing else, so every view here is a fact about the directory: which delivered
|
|
56
|
+
* spec happens to sit beside a module does not change it, and two specs beside one
|
|
57
|
+
* module would compute the same views twice. A module no spec names — deleted by a
|
|
58
|
+
* run that also dropped its import — is still a changed module of its directory,
|
|
59
|
+
* which is what stops un-sharing the helpers from being the way past the gate.
|
|
60
|
+
*
|
|
61
|
+
* No git here. The caller established from git which files were modules at HEAD
|
|
62
|
+
* and what they held; this compares that against the working tree and walks the
|
|
63
|
+
* directory. A module whose working copy cannot be read contributes nothing.
|
|
64
|
+
*/
|
|
65
|
+
export declare function changedHelperCallers(modules: ChangedModule[]): Promise<ChangedHelperView[]>;
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import * as fs from "fs/promises";
|
|
2
|
+
import { logger } from "../../utils/logger.js";
|
|
3
|
+
import { changedHelpersBetween, helperImporters, parseUtilsFile, utilsSpecForFile, } from "../../utils/utils-verify/index.js";
|
|
4
|
+
/**
|
|
5
|
+
* The helpers this run changed in `modules`, each with the tests beside it that
|
|
6
|
+
* import it.
|
|
7
|
+
*
|
|
8
|
+
* PER DIRECTORY, not per spec. The importer walk reads the modules' directory and
|
|
9
|
+
* nothing else, so every view here is a fact about the directory: which delivered
|
|
10
|
+
* spec happens to sit beside a module does not change it, and two specs beside one
|
|
11
|
+
* module would compute the same views twice. A module no spec names — deleted by a
|
|
12
|
+
* run that also dropped its import — is still a changed module of its directory,
|
|
13
|
+
* which is what stops un-sharing the helpers from being the way past the gate.
|
|
14
|
+
*
|
|
15
|
+
* No git here. The caller established from git which files were modules at HEAD
|
|
16
|
+
* and what they held; this compares that against the working tree and walks the
|
|
17
|
+
* directory. A module whose working copy cannot be read contributes nothing.
|
|
18
|
+
*/
|
|
19
|
+
export async function changedHelperCallers(modules) {
|
|
20
|
+
const anchor = modules[0]?.file;
|
|
21
|
+
if (!anchor)
|
|
22
|
+
return [];
|
|
23
|
+
const spec = utilsSpecForFile(anchor);
|
|
24
|
+
if (!spec)
|
|
25
|
+
return [];
|
|
26
|
+
const helpersByFile = new Map();
|
|
27
|
+
const changedByFile = new Map();
|
|
28
|
+
for (const { file, head } of modules) {
|
|
29
|
+
// A module that is GONE reads as empty, which makes every helper it had at HEAD a
|
|
30
|
+
// removal — the same finding as deleting them one at a time. Only absence means
|
|
31
|
+
// that. Any other read failure — a permission error, a transient fault, a
|
|
32
|
+
// directory in its place — is the module not being readable, which is not
|
|
33
|
+
// evidence of anything: treating it as empty would call every incumbent helper
|
|
34
|
+
// removed and demand a run of every caller, on a malfunction.
|
|
35
|
+
let current;
|
|
36
|
+
try {
|
|
37
|
+
current = await fs.readFile(file, "utf8");
|
|
38
|
+
}
|
|
39
|
+
catch (err) {
|
|
40
|
+
if (err?.code !== "ENOENT") {
|
|
41
|
+
logger.warning("Could not read a shared module — the helpers it changed will not be reported", { file, error: String(err) });
|
|
42
|
+
continue;
|
|
43
|
+
}
|
|
44
|
+
current = "";
|
|
45
|
+
}
|
|
46
|
+
const changes = changedHelpersBetween(head, current, spec);
|
|
47
|
+
if (changes.length === 0)
|
|
48
|
+
continue;
|
|
49
|
+
changedByFile.set(file, changes);
|
|
50
|
+
// A REMOVED helper is not in the current module, so a caller set read off that
|
|
51
|
+
// module alone would find none — and report "nothing calls it" about the one
|
|
52
|
+
// change that breaks every caller's import outright. The incumbent's definitions
|
|
53
|
+
// of the removed names go into the walk beside the current ones.
|
|
54
|
+
const helpers = parseUtilsFile(current, spec);
|
|
55
|
+
const gone = new Set(changes.filter((c) => c.change === "removed").map((c) => c.helper));
|
|
56
|
+
helpersByFile.set(file, gone.size === 0
|
|
57
|
+
? helpers
|
|
58
|
+
: [
|
|
59
|
+
...helpers,
|
|
60
|
+
...parseUtilsFile(head, spec).filter((h) => gone.has(h.name)),
|
|
61
|
+
]);
|
|
62
|
+
}
|
|
63
|
+
if (changedByFile.size === 0)
|
|
64
|
+
return [];
|
|
65
|
+
// Anchored on the module: the walk reads the anchor's directory, which is the
|
|
66
|
+
// module's own. A deleted anchor still has a directory.
|
|
67
|
+
const scan = await helperImporters(anchor, [...changedByFile.keys()], helpersByFile, spec);
|
|
68
|
+
const importersOf = new Map(scan.helpers.map((h) => [`${h.file}\0${h.helper}`, h.importers]));
|
|
69
|
+
const out = [];
|
|
70
|
+
for (const [utilsFile, changes] of changedByFile)
|
|
71
|
+
for (const c of changes)
|
|
72
|
+
out.push({
|
|
73
|
+
utilsFile,
|
|
74
|
+
helper: c.helper,
|
|
75
|
+
change: c.change,
|
|
76
|
+
...(scan.read
|
|
77
|
+
? {
|
|
78
|
+
callers: [
|
|
79
|
+
...(importersOf.get(`${utilsFile}\0${c.helper}`) ?? []),
|
|
80
|
+
].sort(),
|
|
81
|
+
}
|
|
82
|
+
: {}),
|
|
83
|
+
});
|
|
84
|
+
return out;
|
|
85
|
+
}
|
|
@@ -2,6 +2,7 @@ import { z } from "zod";
|
|
|
2
2
|
import { fixErrorsInstruction } from "../../skills/fixTestImportErrorsSkill.js";
|
|
3
3
|
import fs from "fs";
|
|
4
4
|
import { logger } from "../../utils/logger.js";
|
|
5
|
+
import { toolError } from "../../utils/utils.js";
|
|
5
6
|
import { ProgrammingLanguage, TestType } from "../../types/TestTypes.js";
|
|
6
7
|
import { ModularizationService, } from "../../services/ModularizationService.js";
|
|
7
8
|
import { AnalyticsService } from "../../services/AnalyticsService.js";
|
|
@@ -9,6 +10,7 @@ import { normalizeLanguageParams, resolveParamAliases, } from "../../utils/norma
|
|
|
9
10
|
import { normalizeSkyrampImportsInFile } from "../../utils/normalizeSkyrampImports.js";
|
|
10
11
|
import { stageGeneratedPaths } from "../../utils/gitStaging.js";
|
|
11
12
|
import { recordModularized } from "./reuse-state.js";
|
|
13
|
+
import { pendingPomPass } from "./pom-pass-state.js";
|
|
12
14
|
const modularizationSchema = {
|
|
13
15
|
testFile: z
|
|
14
16
|
.string()
|
|
@@ -82,6 +84,14 @@ After modularization, if errors remain, ${fixErrorsInstruction()}.
|
|
|
82
84
|
if (!params.isTraceBased &&
|
|
83
85
|
[TestType.UI, TestType.E2E, TestType.INTEGRATION].includes(params.testType))
|
|
84
86
|
params.isTraceBased = true;
|
|
87
|
+
// Both reuse settings on: the page-object pass maps the raw steps first, and
|
|
88
|
+
// its gates judge the spec while what it did not substitute is still inline.
|
|
89
|
+
// Checked before anything is recorded, so a refused call leaves no mark.
|
|
90
|
+
const owedPomPass = await pendingPomPass(params.testFile);
|
|
91
|
+
if (owedPomPass) {
|
|
92
|
+
errorResult = toolError(owedPomPass);
|
|
93
|
+
return errorResult;
|
|
94
|
+
}
|
|
85
95
|
normalizeSkyrampImportsInFile(params.testFile);
|
|
86
96
|
// Stage so testbot includes the generated files in its output commit.
|
|
87
97
|
await stageGeneratedPaths(params.testFile);
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import type { ReuseHandOff } from "../../utils/AnalysisStateManager.js";
|
|
2
|
+
import type { ReuseRederivation } from "./reuse-state.js";
|
|
3
|
+
/**
|
|
4
|
+
* The page-object pass as run state, for a target both reuse settings serve
|
|
5
|
+
* (isPomFirstTarget): whether the pass has finished for a spec, the mark that
|
|
6
|
+
* finishes it, the checkpoint that holds modularization until it has, and the
|
|
7
|
+
* merge of the two halves one such spec's report row carries.
|
|
8
|
+
*/
|
|
9
|
+
/** Whether a hand-off's chain starts with the page-object pass. Derived from what the
|
|
10
|
+
* hand-off already stores, never recorded beside it: generation writes the hand-off
|
|
11
|
+
* from exactly these three values, and maintenance arms none for such a target. */
|
|
12
|
+
export declare function pomFirstHandOff(handOff: ReuseHandOff): boolean;
|
|
13
|
+
/**
|
|
14
|
+
* With both reuse settings on (isPomFirstTarget), where this spec's page-object pass
|
|
15
|
+
* stands. `undefined` while it is open; `"finished"` once its verify pass PASSED;
|
|
16
|
+
* `"no-layer"` when discovery found no page-object layer to reuse. From then on the
|
|
17
|
+
* reuse tool serves the SkyrampUtils flow for the spec, and the page-object gates
|
|
18
|
+
* have already judged the spec while every step they did not substitute was still raw
|
|
19
|
+
* and inline — the state they were written for.
|
|
20
|
+
*
|
|
21
|
+
* A FAILED verdict keeps the pass open: the page-object report says to fix and
|
|
22
|
+
* re-verify, and `pendingReuseVerification` refuses the tools after it until that
|
|
23
|
+
* happens. A record owned by another spec, or no run state, reads as open; the caller
|
|
24
|
+
* then serves the page-object pass, which is what the spec gets without composition.
|
|
25
|
+
*/
|
|
26
|
+
export declare function pomPassOutcome(testFile: string, explicitStateFile?: string): Promise<"finished" | "no-layer" | undefined>;
|
|
27
|
+
/** Mark the page-object pass finished for a spec (see `pomPassOutcome`). Written by
|
|
28
|
+
* the reuse tool at the two points that finish it: a PASSED page-object verify, and
|
|
29
|
+
* discovery that found no page-object layer. */
|
|
30
|
+
export declare function recordPomPassFinished(testFile: string, explicitStateFile?: string): Promise<void>;
|
|
31
|
+
/**
|
|
32
|
+
* Does this spec owe its page-object pass before `skyramp_modularization`? Only a spec
|
|
33
|
+
* generation handed off with the page-object pass first. Modularizing first would move
|
|
34
|
+
* the raw steps the page-object pass maps into helpers it cannot see, and its gates
|
|
35
|
+
* would then judge a spec with nothing left inline. Fails open without a hand-off, on
|
|
36
|
+
* an unreadable or unwritable state file.
|
|
37
|
+
*/
|
|
38
|
+
export declare function pendingPomPass(testFile: string, explicitStateFile?: string): Promise<string | undefined>;
|
|
39
|
+
/**
|
|
40
|
+
* One row, both halves. The outcome fields are disjoint by construction (the
|
|
41
|
+
* page-object fields sit at the top level, the shared-helper ones under `helpers` and
|
|
42
|
+
* `inHouse`). The two blocking verdicts become ONE, because the report refuses per
|
|
43
|
+
* spec and one `verify: true` call re-checks both halves (the reuse tool's combined
|
|
44
|
+
* verify). Declines are retired only where the half that owns them was measured: the
|
|
45
|
+
* page-object half measures a `kept inline` decline only when its zero-reuse gate
|
|
46
|
+
* walked the candidates, which it does not at non-zero reuse.
|
|
47
|
+
*/
|
|
48
|
+
export declare function combineRederivations(pom: ReuseRederivation, helper: ReuseRederivation, carriesKeptInline: boolean,
|
|
49
|
+
/** The call that re-checks both halves — the shared-helper verify call, which the
|
|
50
|
+
* reuse tool routes to its combined verify for such a spec. */
|
|
51
|
+
combinedVerifyCall: string | undefined): ReuseRederivation;
|