@skyramp/mcp 0.3.1 → 0.3.2-rc.pom-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/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 +98 -52
- 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 +3 -0
- package/build/tools/code-refactor/codeReuseTool.js +76 -4
- package/build/tools/code-refactor/codeReuseTool.test.d.ts +1 -0
- package/build/tools/code-refactor/codeReuseTool.test.js +51 -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
package/build/index.js
CHANGED
|
@@ -24,7 +24,7 @@ import { registerCodeReuseTool } from "./tools/code-refactor/codeReuseTool.js";
|
|
|
24
24
|
import { registerEnhanceAssertionsTool } from "./tools/code-refactor/enhanceAssertionsTool.js";
|
|
25
25
|
import { registerBatchScenarioTestTool } from "./tools/generate-tests/generateBatchScenarioRestTool.js";
|
|
26
26
|
import { registerMockTool } from "./tools/generate-tests/generateMockRestTool.js";
|
|
27
|
-
import { registerAnalyzeChangesTool, registerAnalyzeTestHealthTool, registerActionsTool, } from "./tools/test-management/index.js";
|
|
27
|
+
import { registerAnalyzeChangesTool, registerAnalyzeTestHealthTool, registerActionsTool, registerRegisterTestPlanTool, } from "./tools/test-management/index.js";
|
|
28
28
|
import { registerTestbotPrompt } from "./prompts/testbot/testbot-prompts.js";
|
|
29
29
|
import { registerSutSetupResource } from "./resources/sutSetupResource.js";
|
|
30
30
|
import { registerTestbotResource } from "./resources/testbotResource.js";
|
|
@@ -151,6 +151,7 @@ registerProgressResource(server);
|
|
|
151
151
|
registerAnalyzeChangesTool(server);
|
|
152
152
|
registerAnalyzeTestHealthTool(server);
|
|
153
153
|
registerActionsTool(server);
|
|
154
|
+
registerRegisterTestPlanTool(server);
|
|
154
155
|
// Register workspace management tools
|
|
155
156
|
registerInitScanWorkspaceTool(server);
|
|
156
157
|
registerInitializeWorkspaceTool(server);
|
|
@@ -1 +1,7 @@
|
|
|
1
|
-
|
|
1
|
+
import type { ScopedPom } from "../utils/pom-scope/index.js";
|
|
2
|
+
export declare function isPomAwareTarget(language: string, framework?: string): boolean;
|
|
3
|
+
export declare function getCodeReusePrompt(testFile: string, language: string, framework?: string, scopedPoms?: {
|
|
4
|
+
tier1: ScopedPom[];
|
|
5
|
+
tier2: ScopedPom[];
|
|
6
|
+
scannedNoOverlap?: boolean;
|
|
7
|
+
}): string;
|
|
@@ -18,11 +18,14 @@ const LANGUAGE_MAP = {
|
|
|
18
18
|
fileName: "skyrampUtils.ts",
|
|
19
19
|
},
|
|
20
20
|
};
|
|
21
|
-
export function
|
|
22
|
-
const
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
21
|
+
export function isPomAwareTarget(language, framework) {
|
|
22
|
+
const lang = language.toLowerCase();
|
|
23
|
+
return (lang === "typescript" || lang === "javascript") && framework?.toLowerCase() === "playwright";
|
|
24
|
+
}
|
|
25
|
+
export function getCodeReusePrompt(testFile, language, framework, scopedPoms) {
|
|
26
|
+
const lang = language.toLowerCase();
|
|
27
|
+
if (isPomAwareTarget(lang, framework)) {
|
|
28
|
+
return getPomAwareCodeReusePrompt(testFile, lang, scopedPoms);
|
|
26
29
|
}
|
|
27
30
|
const ext = LANGUAGE_MAP[language].extension || "py";
|
|
28
31
|
const fileName = LANGUAGE_MAP[language].fileName || "SkyrampUtils.py";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { getCodeReusePrompt } from "./code-reuse.js";
|
|
2
|
+
const POM = "POM-AWARE CODE REUSE"; // unique to the POM branch
|
|
3
|
+
const pom = (file, score = 2, rareHits = 2, matchedTokens = ["a-b-c"]) => ({ file, score, rareHits, matchedTokens });
|
|
4
|
+
describe("getCodeReusePrompt routing", () => {
|
|
5
|
+
it("routes javascript + playwright to the POM-aware prompt", () => {
|
|
6
|
+
expect(getCodeReusePrompt("t.spec.js", "javascript", "playwright")).toContain(POM);
|
|
7
|
+
});
|
|
8
|
+
it("still routes typescript + playwright to the POM-aware prompt", () => {
|
|
9
|
+
expect(getCodeReusePrompt("t.spec.ts", "typescript", "playwright")).toContain(POM);
|
|
10
|
+
});
|
|
11
|
+
it("routes javascript + non-playwright to SkyrampUtils", () => {
|
|
12
|
+
expect(getCodeReusePrompt("t.spec.js", "javascript", "jest")).not.toContain(POM);
|
|
13
|
+
});
|
|
14
|
+
it("routes python to SkyrampUtils (no regression)", () => {
|
|
15
|
+
expect(getCodeReusePrompt("t.py", "python", "pytest")).not.toContain(POM);
|
|
16
|
+
});
|
|
17
|
+
it("does not trigger POM path when framework is undefined", () => {
|
|
18
|
+
expect(getCodeReusePrompt("t.spec.ts", "typescript")).not.toContain(POM);
|
|
19
|
+
});
|
|
20
|
+
});
|
|
21
|
+
describe("getCodeReusePrompt scoped POM list", () => {
|
|
22
|
+
it("emits the pre-detected two-tier STEP 1 when scoped files are supplied", () => {
|
|
23
|
+
const p = getCodeReusePrompt("t.spec.js", "javascript", "playwright", { tier1: [pom("/repo/pages/loginPage.js")], tier2: [pom("/repo/pages/homePage.js", 0.3, 1, ["menu-link-/alerts-item"])] });
|
|
24
|
+
expect(p).toContain("ALREADY DETECTED");
|
|
25
|
+
expect(p).toContain("/repo/pages/loginPage.js");
|
|
26
|
+
expect(p).toContain("catalog fully");
|
|
27
|
+
expect(p).toContain("/repo/pages/homePage.js");
|
|
28
|
+
expect(p).toContain("signature level");
|
|
29
|
+
expect(p).toContain("menu-link-/alerts-item"); // matched tokens surface for tier-2 mapping evidence
|
|
30
|
+
expect(p).not.toContain("DETECT POM CLASSES");
|
|
31
|
+
});
|
|
32
|
+
it("emits the glob STEP 1 when no scoped files are supplied", () => {
|
|
33
|
+
expect(getCodeReusePrompt("t.spec.js", "javascript", "playwright")).toContain("DETECT POM CLASSES");
|
|
34
|
+
});
|
|
35
|
+
it("emits the glob STEP 1 when both tiers are empty (fallback)", () => {
|
|
36
|
+
expect(getCodeReusePrompt("t.spec.js", "javascript", "playwright", { tier1: [], tier2: [] }))
|
|
37
|
+
.toContain("DETECT POM CLASSES");
|
|
38
|
+
});
|
|
39
|
+
it("returns the short skip response on a clean scan with zero overlap", () => {
|
|
40
|
+
const p = getCodeReusePrompt("t.spec.js", "javascript", "playwright", {
|
|
41
|
+
tier1: [], tier2: [], scannedNoOverlap: true,
|
|
42
|
+
});
|
|
43
|
+
expect(p).toContain("No reusable POM layer detected");
|
|
44
|
+
expect(p).toContain("verify: true"); // escape hatch mandates re-verify
|
|
45
|
+
expect(p).not.toContain("COMPLIANCE RULES"); // NOT the full workflow prompt
|
|
46
|
+
expect(p.length).toBeLessThan(2000);
|
|
47
|
+
});
|
|
48
|
+
it("keeps the full agent-driven fallback when scoping was inconclusive", () => {
|
|
49
|
+
// no scopedPoms argument = scan didn't run or didn't conclude
|
|
50
|
+
const p = getCodeReusePrompt("t.spec.js", "javascript", "playwright");
|
|
51
|
+
expect(p).toContain("COMPLIANCE RULES");
|
|
52
|
+
expect(p).not.toContain("No reusable POM layer detected");
|
|
53
|
+
});
|
|
54
|
+
it("leaves the SkyrampUtils path untouched for non-Playwright languages", () => {
|
|
55
|
+
const p = getCodeReusePrompt("t.py", "python", undefined, { tier1: [], tier2: [], scannedNoOverlap: true });
|
|
56
|
+
expect(p).not.toContain("No reusable POM layer detected");
|
|
57
|
+
expect(p).toContain("SkyrampUtils");
|
|
58
|
+
});
|
|
59
|
+
it("does not annotate when scoping simply was not attempted", () => {
|
|
60
|
+
expect(getCodeReusePrompt("t.spec.js", "javascript", "playwright")).not.toContain("No reusable POM layer detected");
|
|
61
|
+
});
|
|
62
|
+
});
|
|
@@ -1 +1,6 @@
|
|
|
1
|
-
|
|
1
|
+
import type { ScopedPom } from "../utils/pom-scope/index.js";
|
|
2
|
+
export declare function getPomAwareCodeReusePrompt(testFile: string, language?: string, scopedPoms?: {
|
|
3
|
+
tier1: ScopedPom[];
|
|
4
|
+
tier2: ScopedPom[];
|
|
5
|
+
scannedNoOverlap?: boolean;
|
|
6
|
+
}): string;
|
|
@@ -1,8 +1,52 @@
|
|
|
1
1
|
import { generateSkyrampHeader, SKYRAMP_UTILS_HEADER } from "../utils/utils.js";
|
|
2
|
-
|
|
3
|
-
const
|
|
4
|
-
|
|
5
|
-
|
|
2
|
+
export function getPomAwareCodeReusePrompt(testFile, language = "typescript", scopedPoms) {
|
|
3
|
+
const isJs = language.toLowerCase() === "javascript";
|
|
4
|
+
const ext = isJs ? "js" : "ts";
|
|
5
|
+
const utilsFile = isJs ? "skyrampUtils.js" : "skyrampUtils.ts";
|
|
6
|
+
const codegenMarker = "// Generated by Skyramp v";
|
|
7
|
+
const headerLang = isJs ? "javascript" : "typescript";
|
|
8
|
+
const scopedActive = scopedPoms && scopedPoms.tier1.length + scopedPoms.tier2.length > 0;
|
|
9
|
+
if (!scopedActive && scopedPoms?.scannedNoOverlap) {
|
|
10
|
+
return `No reusable POM layer detected: a repo-wide scan found zero overlap between this test's selectors and any non-test source file. Code reuse is skipped — continue with the generated test as-is.
|
|
11
|
+
|
|
12
|
+
Caveat: detection matches literal selector tokens; a POM layer built on computed/dynamic selectors could be missed. If you have concrete evidence of one (e.g. a pageobjects/ directory with classes covering this flow), you may refactor ${testFile} manually following the repo's existing spec conventions — and if you do, you MUST call this tool again with \`verify: true\` and follow its report before finishing. Do NOT call skyramp_modularization.`;
|
|
13
|
+
}
|
|
14
|
+
const renderPom = (p) => `- \`${p.file}\`${p.matchedTokens.length > 0 ? ` — matched: ${p.matchedTokens.map((t) => `\`${t}\``).join(", ")}` : ""}`;
|
|
15
|
+
const step1Section = scopedActive
|
|
16
|
+
? `## STEP 1: POM FILES — ALREADY DETECTED
|
|
17
|
+
|
|
18
|
+
POM detection has been performed programmatically from this test's selectors.
|
|
19
|
+
|
|
20
|
+
**Tier 1 — high relevance (catalog fully — every property and method):**
|
|
21
|
+
${scopedPoms.tier1.map(renderPom).join("\n") || "(none)"}
|
|
22
|
+
|
|
23
|
+
**Tier 2 — supporting/possible (catalog at signature level ONLY — class name, method names, and the matched selectors shown; deepen a file only if STEP 2b maps an action to it):**
|
|
24
|
+
${scopedPoms.tier2.map(renderPom).join("\n") || "(none)"}
|
|
25
|
+
|
|
26
|
+
**Caution on matched tokens:** the tokens above are raw substring evidence from the detector, not a guarantee of the file's full frame id — a matched token like \`#application-new-applied\` can hide that the file's ACTUAL frame is a prefix-sibling such as \`#application-new-applied-type\`. When you catalog each file in STEP 2, record its ACTUAL full selector/frame id from source rather than the bare matched token.
|
|
27
|
+
|
|
28
|
+
Do NOT glob or scan for other POM files. Proceed to STEP 2 and catalog exactly these files (plus any file they import or extend that you find essential while reading them).
|
|
29
|
+
|
|
30
|
+
**Catalog-completeness guard for STEP 2a:** when checking for an existing \`skyramp-pom-catalog.md\`, it is only valid for THIS run if it already contains an entry for EVERY file listed above. If any listed file is missing from the catalog, do NOT reuse it wholesale — keep its existing entries and generate + append entries for the missing listed files (then use the updated catalog).
|
|
31
|
+
|
|
32
|
+
**Decision:** continue to STEP 2.`
|
|
33
|
+
: `## STEP 1: DETECT POM CLASSES IN THE PROJECT
|
|
34
|
+
|
|
35
|
+
Use the Glob tool to search for POM files starting from the directory containing \`${testFile}\`:
|
|
36
|
+
- Pattern 1: \`**/pageobjects/**/*.{ts,js}\`
|
|
37
|
+
- Pattern 2: \`**/page-objects/**/*.{ts,js}\`
|
|
38
|
+
- Pattern 3: \`**/pages/**/*.{ts,js}\`
|
|
39
|
+
- Pattern 4: \`**/*.page.{ts,js}\`
|
|
40
|
+
- Pattern 5: \`**/*Page.{ts,js}\`
|
|
41
|
+
- Pattern 6: \`**/pom/**/*.{ts,js}\`
|
|
42
|
+
|
|
43
|
+
Exclude test/spec files by **basename only** — never match on the directory path. POM layers commonly live under a \`tests/\` directory (e.g. \`tests/ui-testing/pages/\`), and path-matching would wrongly drop every POM. A file is excluded only if its **basename** ends in \`.spec.ts\`, \`.spec.js\`, \`.test.ts\`, or \`.test.js\`. Do NOT exclude on the bare substrings \`test\` or \`spec\`: they falsely match legitimate POMs (e.g. \`searchJobInspectorPage.js\` contains "spec") and directory names (\`tests/\`).
|
|
44
|
+
Also exclude \`node_modules\`.
|
|
45
|
+
|
|
46
|
+
**Decision:**
|
|
47
|
+
- Zero matches found → **SKIP TO STEP 6 (FALLBACK)**
|
|
48
|
+
- One or more matches found → continue to STEP 2`;
|
|
49
|
+
return `# POM-AWARE CODE REUSE — TYPESCRIPT/JAVASCRIPT + PLAYWRIGHT
|
|
6
50
|
|
|
7
51
|
## COMPLIANCE RULES — READ THIS FIRST, APPLY TO EVERY STEP
|
|
8
52
|
|
|
@@ -27,22 +71,7 @@ This is NOT the same as SkyrampUtils consolidation. Do NOT create a SkyrampUtils
|
|
|
27
71
|
|
|
28
72
|
---
|
|
29
73
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
Use the Glob tool to search for POM files starting from the directory containing \`${testFile}\`:
|
|
33
|
-
- Pattern 1: \`**/pageobjects/**/*.ts\`
|
|
34
|
-
- Pattern 2: \`**/page-objects/**/*.ts\`
|
|
35
|
-
- Pattern 3: \`**/pages/**/*.ts\`
|
|
36
|
-
- Pattern 4: \`**/*.page.ts\`
|
|
37
|
-
- Pattern 5: \`**/*Page.ts\`
|
|
38
|
-
- Pattern 6: \`**/pom/**/*.ts\`
|
|
39
|
-
|
|
40
|
-
Exclude any file with \`test\`, \`spec\`, \`.test.\`, or \`.spec.\` in its name.
|
|
41
|
-
Also exclude \`node_modules\`.
|
|
42
|
-
|
|
43
|
-
**Decision:**
|
|
44
|
-
- Zero matches found → **SKIP TO STEP 6 (FALLBACK)**
|
|
45
|
-
- One or more matches found → continue to STEP 2
|
|
74
|
+
${step1Section}
|
|
46
75
|
|
|
47
76
|
---
|
|
48
77
|
|
|
@@ -50,7 +79,7 @@ Also exclude \`node_modules\`.
|
|
|
50
79
|
|
|
51
80
|
**Step 2a: Check for existing catalog (MANDATORY FIRST STEP)**
|
|
52
81
|
|
|
53
|
-
Check if \`skyramp-pom-catalog.md\` exists in the same directory as \`tsconfig.json
|
|
82
|
+
Check if \`skyramp-pom-catalog.md\` exists in the same directory as \`tsconfig.json\` (or \`jsconfig.json\`, or the test root if neither exists):
|
|
54
83
|
- **If it exists:** Read it and verify it starts with \`<!-- skyramp-pom-catalog: auto-generated -->\`
|
|
55
84
|
- Valid → Output: *"Using existing POM catalog from /path/to/skyramp-pom-catalog.md"* and **skip to STEP 2b**
|
|
56
85
|
- Invalid → Continue to full catalog generation below
|
|
@@ -62,12 +91,17 @@ Check if \`skyramp-pom-catalog.md\` exists in the same directory as \`tsconfig.j
|
|
|
62
91
|
|
|
63
92
|
Before writing a single line of refactored code, do the following:
|
|
64
93
|
|
|
65
|
-
|
|
94
|
+
1. **Extract conventions from ONE existing test** — Open one existing test spec (prefer one that imports the POM layer). Extract ONLY: (a) the instantiation convention (central PageManager, e.g. \`const pm = new PageManager(page)\`, vs direct \`new X(page)\`), (b) import style and path aliases, (c) the section-comment style. Ignore everything else in that file — do NOT mimic its test logic, waits, or assertions.
|
|
66
95
|
|
|
67
96
|
2. **Read tsconfig.json / jsconfig.json** — Capture path alias conventions (e.g., \`@pageobjects/*\`, \`@common-function/*\`). All imports in the refactored test must use these aliases exactly.
|
|
68
97
|
|
|
69
|
-
3. **Read
|
|
98
|
+
3. **Read the POM files from STEP 1** — Depth depends on how STEP 1 listed them: files marked **Tier 2 (signature level)** get a REDUCED entry — class name, method names (signatures only), and the matched selectors STEP 1 showed; do NOT read their full bodies or record per-method timing/network details unless STEP 2b later maps an action to that file (then deepen just that file). **Tier 1 files — and ALL files when STEP 1 did not use tiers (agent-detected mode)** — get the FULL treatment below. For each full-depth file, document:
|
|
70
99
|
- Class name, import path (using tsconfig aliases), and what page/component it represents
|
|
100
|
+
**Locator styles to capture — a POM class may expose selectors in ANY of these forms; catalog all of them, recording the selector string passed to \`.locator()\` / \`getByRole\` / \`getByTestId\` etc.:**
|
|
101
|
+
- Constructor-assigned: \`this.loginButton = page.locator('[data-test="login-sign-in"]')\`
|
|
102
|
+
- Getter accessors: \`get firstRow() { return this.page.locator('[data-test^="row-"]').first(); }\`
|
|
103
|
+
- Object-literal maps: \`this.locators = { menuItem: '[data-test="menu-item"]', ... }\` (each key is a named selector)
|
|
104
|
+
- Inline in methods: a \`.locator(...)\` used directly inside an action method (still a reusable target, named by its method)
|
|
71
105
|
- Iframe selector used in the constructor (e.g. \`page.frameLocator('iframe#asset-list')\`), or "top-level page" if no iframe
|
|
72
106
|
- Every property (locator): name and its exact selector string from the constructor assignment
|
|
73
107
|
- Every method: name, parameters, what action it performs
|
|
@@ -77,7 +111,7 @@ can 1. **Read existing refactored tests** — Find any already-refactored test f
|
|
|
77
111
|
- Whether the method wraps a multi-step sequence using sub-properties or components (e.g. \`searchAssetsByCondition\` internally calls \`searchConditionArea.expandWhenClosed()\`, \`clearBtn.click()\`, \`selectItemName()\`, \`valueKeyword.fill()\`, \`searchBtn.click()\`) — flag these as **wrapping methods**
|
|
78
112
|
- Flag methods as **timing-aware**, **network-aware**, or **wrapping** as appropriate — all three are high-value substitution targets
|
|
79
113
|
|
|
80
|
-
4. **Write the POM catalog — MANDATORY** — Write the complete catalog to \`skyramp-pom-catalog.md\` in the same directory as \`tsconfig.json
|
|
114
|
+
4. **Write the POM catalog — MANDATORY** — Write the complete catalog to \`skyramp-pom-catalog.md\` in the same directory as \`tsconfig.json\` (or \`jsconfig.json\`, or the test root if neither exists). This gives you a clean structured reference for STEP 2b and leaves a record for debugging.
|
|
81
115
|
|
|
82
116
|
**BLOCKING: Do not proceed to STEP 2b until you have written this file and confirmed the path in a single output line:**
|
|
83
117
|
*"POM catalog written to /path/to/skyramp-pom-catalog.md"*
|
|
@@ -98,13 +132,15 @@ Use this exact format for the file:
|
|
|
98
132
|
- \`methodName(param1, param2)\` — description; ⏱ timing-aware: waitForTimeout(Xms) + sleep(Ys) | 🌐 network-aware: waitForResponse('**/pattern**') | (omit tags if neither)
|
|
99
133
|
\`\`\`
|
|
100
134
|
|
|
135
|
+
**Tier-2 entries in the catalog:** for files cataloged at signature level, the entry contains the class name, the import path, a \`- **Methods:**\` list of names/signatures only, and the matched selectors from STEP 1 under \`- **Properties:**\`. Mark such entries with \`<!-- depth: signature -->\` on the line after the class heading so a later run knows the entry is shallow.
|
|
136
|
+
|
|
101
137
|
---
|
|
102
138
|
|
|
103
139
|
## STEP 2b: BUILD AND OUTPUT A MAPPING TABLE
|
|
104
140
|
|
|
105
141
|
**This step is MANDATORY. Do not write any code until it is complete.**
|
|
106
142
|
|
|
107
|
-
For every raw locator and action sequence in \`${testFile}\`, find the best POM match and
|
|
143
|
+
For every raw locator and action sequence in \`${testFile}\`, find the best POM match; record the PREFERRED and HIGH matches in a table (all others simply remain inline). Output the complete table to chat before proceeding to STEP 3.
|
|
108
144
|
|
|
109
145
|
### Matching criteria
|
|
110
146
|
|
|
@@ -138,6 +174,8 @@ Before considering any POM match, verify the iframe context matches:
|
|
|
138
174
|
|
|
139
175
|
### Mapping table format
|
|
140
176
|
|
|
177
|
+
List ONLY rows with PREFERRED or HIGH confidence — these are the substitutions you will make. Do NOT enumerate LOW/UNCERTAIN or UNMAPPED locators in the table; every locator not in the table remains inline as-is.
|
|
178
|
+
|
|
141
179
|
Output the table in this format:
|
|
142
180
|
|
|
143
181
|
\`\`\`
|
|
@@ -145,16 +183,16 @@ Output the table in this format:
|
|
|
145
183
|
|---|---|---|---|---|
|
|
146
184
|
| goto + fill email/password + click login | LoginPage | login(email, password) | method is timing-aware (waitForTimeout+sleep) covering login sequence | PREFERRED |
|
|
147
185
|
| frameLocator('iframe#asset-list').locator('button:has-text("資産登録")') | AssetListPage | assetEntryBtn | iframe.locator('button:has-text("資産登録")') | HIGH |
|
|
148
|
-
| page.locator('input[name="email"]').fill(...) | LoginPage | emailInput | getByPlaceholder('メールアドレス(半角)') | LOW |
|
|
149
|
-
| frameLocator('iframe#wf-input').locator('input[name="changeQty"]') | — | — | No match | UNMAPPED |
|
|
150
186
|
\`\`\`
|
|
151
187
|
|
|
188
|
+
All other locators remain inline.
|
|
189
|
+
|
|
152
190
|
### Rules after completing the table
|
|
153
191
|
|
|
154
192
|
- **PREFERRED entries MUST be substituted** — use the POM method and drop the raw test's explicit waits/correlations that fall inside the method's scope
|
|
155
193
|
- **HIGH entries MUST be substituted** — use the POM method or property; do NOT write an inline locator
|
|
156
194
|
- **Always prefer the highest level of abstraction available** — if a wrapping method exists that covers a multi-step sequence, use it instead of calling the individual sub-properties directly. Example: use \`searchAssetsByCondition("資産名", assetName)\` not \`searchConditionArea.expandWhenClosed()\` + \`getConditionRow(1)\` + \`selectItemName()\` etc. Use \`bulkDelete()\` not \`bulkDeleteBtn.click()\` + \`confirmDeleteDialog\` + \`noticeDialog\` inline.
|
|
157
|
-
- **Do NOT upgrade LOW or UNCERTAIN** because the substitution seems reasonable
|
|
195
|
+
- **Do NOT upgrade LOW or UNCERTAIN to PREFERRED/HIGH** because the substitution seems reasonable — leave them inline and out of the table
|
|
158
196
|
- **Conservative default**: an incorrect substitution that breaks the test is worse than leaving something inline — when in doubt, leave it inline
|
|
159
197
|
- If the table has ZERO PREFERRED or HIGH entries → **SKIP TO STEP 6 (FALLBACK)**
|
|
160
198
|
- If AT LEAST ONE PREFERRED or HIGH entry exists → continue to STEP 3
|
|
@@ -264,6 +302,8 @@ When a module operates inside an iframe, determine the correct frame handle by r
|
|
|
264
302
|
|
|
265
303
|
**Never create standalone \`frameLocator()\` references in the test body.** If an action is UNMAPPED and must stay inline, access the frame through the POM's own \`iframe\` property (e.g. \`assetSelectPage.iframe.locator(...)\`, \`inputPage.iframe.getByRole(...)\`). Do not create a separate \`const inputPageFrame = skyrampPage.frameLocator('#application-new-applied')\` — this duplicates the frame reference that already exists in the POM.
|
|
266
304
|
|
|
305
|
+
**Exception — orphan frame:** if NO cataloged POM class scopes to the frame a raw action uses, there is no POM \`iframe\` property to borrow. In that case a standalone inline \`frameLocator(...)\` for that action is CORRECT — keep it, and mark it with a brief comment (e.g. \`// no POM owns this frame — kept inline\`). Do not force-fit a method from a different-frame class (the gate above still forbids that) and do not invent a POM to cover it.
|
|
306
|
+
|
|
267
307
|
### 3h. Dynamic Locators — Prefer Positional Module Methods
|
|
268
308
|
|
|
269
309
|
Before keeping a dynamic locator inline, check whether a module method exists that targets the same element by position rather than by value. If one exists, use it with the appropriate position (typically \`0\` for the first result) and add a comment noting it replaces the original dynamic locator. If no positional equivalent exists, keep the raw dynamic locator inline as-is.
|
|
@@ -278,6 +318,12 @@ If the existing POM files have an established pattern for checkbox interactions,
|
|
|
278
318
|
|
|
279
319
|
Screenshot assertions (\`toHaveScreenshot\`) must stay in their **exact original position** relative to surrounding actions from the raw test. Note what actions appear immediately before and after the screenshot in the raw test, then place the screenshot at that same relative position in the refactored test. If a POM method would move the screenshot, inline that method's steps around it with a comment: \`// POM method not used — screenshot position must be preserved\`
|
|
280
320
|
|
|
321
|
+
### 3k. Page Object Instantiation — Follow the Repo's Convention
|
|
322
|
+
|
|
323
|
+
**Instantiation convention — follow the repo's existing pattern (read the existing specs to detect it):**
|
|
324
|
+
- If the repo uses a central PageManager (e.g. \`const pm = new PageManager(page)\` then \`pm.loginPage.login()\`), the refactored test MUST use that accessor convention — do NOT instantiate page objects directly.
|
|
325
|
+
- Otherwise, instantiate page objects directly, matching what the existing specs do.
|
|
326
|
+
|
|
281
327
|
---
|
|
282
328
|
|
|
283
329
|
## STEP 4: WRITE THE REFACTORED TEST
|
|
@@ -288,13 +334,11 @@ Using your catalog from STEP 2 and analysis from STEP 3, produce the refactored
|
|
|
288
334
|
Before writing each line of refactored code, look up that action in your STEP 2b mapping table:
|
|
289
335
|
- If it is marked **PREFERRED** → use the POM method and drop any raw waits/correlations inside its scope. This is the highest-value substitution.
|
|
290
336
|
- If it is marked **HIGH** → you MUST use the POM method or property. Do NOT write an inline locator.
|
|
291
|
-
- If it is
|
|
337
|
+
- If it is NOT in the table (LOW / UNCERTAIN / UNMAPPED) → write it inline exactly as in the original test.
|
|
292
338
|
|
|
293
|
-
|
|
339
|
+
A locator with a PREFERRED/HIGH mapping that is written inline anyway is a DEFECT — the customer reads that as a missed reuse.
|
|
294
340
|
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
Before proceeding to STEP 5, scan your refactored test output and for each HIGH entry in the mapping table confirm the corresponding POM call is present in the output. If any HIGH entry is missing its POM call and was written as inline code instead, go back and fix it before writing the file.
|
|
341
|
+
Do not skip this lookup for any action. The mapping table is your source of truth — not the raw test's locators.
|
|
298
342
|
|
|
299
343
|
**Section Comments:** Divide the test body into clearly labeled sections corresponding to the logical phases of the user journey. Use the comment style from the repository's existing tests. If none exists, use \`/*----- Section Name -----*/\`.
|
|
300
344
|
|
|
@@ -308,17 +352,19 @@ Before proceeding to STEP 5, scan your refactored test output and for each HIGH
|
|
|
308
352
|
- Screenshot assertions (\`toHaveScreenshot\`): always keep inline; if mid-method sequence, inline that method's steps around it
|
|
309
353
|
- Page readiness checks: use module methods such as \`pageToBeVisible()\` if they exist
|
|
310
354
|
|
|
311
|
-
**IMPORTANT:** Do NOT create a \`
|
|
355
|
+
**IMPORTANT:** Do NOT create a \`${utilsFile}\` or any SkyrampUtils file on this path. Do NOT call \`skyramp_modularization\` after completing POM-aware code reuse — POM refactoring replaces that step.
|
|
312
356
|
|
|
313
357
|
---
|
|
314
358
|
|
|
315
359
|
## STEP 5: WRITE THE OUTPUT
|
|
316
360
|
|
|
317
|
-
Write the complete, runnable refactored test directly to \`${testFile}\`, overwriting it in place. The file must contain every line — never use placeholder comments like \`// ...continue\` or \`// ...existing code\`. If the test is long, write it in multiple passes until the file is complete. Confirm the file path written to in a single line once done.
|
|
361
|
+
Write the complete, runnable refactored test directly to \`${testFile}\`, overwriting it in place. Before overwriting, save the current (pre-refactor) content to \`${testFile}.raw.bak\` — verification uses it to restore demoted substitutions. The file must contain every line — never use placeholder comments like \`// ...continue\` or \`// ...existing code\`. If the test is long, write it in multiple passes until the file is complete. Confirm the file path written to in a single line once done.
|
|
318
362
|
|
|
319
363
|
Do not output a section breakdown, modularization summary, or any analysis to the chat. Perform all analysis internally.
|
|
320
364
|
|
|
321
|
-
**
|
|
365
|
+
**A low substitution count can be the correct outcome.** When many actions stay inline because the POM layer simply does not model that flow (iframe-gate rejections, orphan frames, vocabulary drift from the cataloged classes), that is honest reuse, not a shortfall — do not pad the mapping table to inflate the count. In your one-line confirmation, state how many actions were substituted vs. kept inline and why in one clause (e.g. "6 substituted; 9 kept inline — POM layer does not model the apply-from-asset flow").
|
|
366
|
+
|
|
367
|
+
**Do NOT run the test.** After writing the file, you MUST call \`skyramp_reuse_code\` once more with the same \`testFile\`/\`language\`/\`framework\` and \`verify: true\`, and follow its report: re-verify after every fix pass; a violation may be remapped at most once — if flagged again, demote it to the raw inline form; finish only when it reports PASSED (unverifiable notes are acceptable). Include \`verification: passed\` (or the unresolved count) in your final confirmation line.
|
|
322
368
|
|
|
323
369
|
---
|
|
324
370
|
|
|
@@ -360,7 +406,7 @@ Read ${testFile} and look for ALREADY DEFINED helper functions (not just repetit
|
|
|
360
406
|
## STEP 2: FIND EXISTING UTILS
|
|
361
407
|
Use the Grep tool to search for files containing "${SKYRAMP_UTILS_HEADER}":
|
|
362
408
|
- Pattern: "${SKYRAMP_UTILS_HEADER}"
|
|
363
|
-
- Type: "
|
|
409
|
+
- Type: "${ext}"
|
|
364
410
|
- Output mode: "files_with_matches"
|
|
365
411
|
**CRITICAL: Only look for util files with header ${SKYRAMP_UTILS_HEADER}** - exclude test files (files with "test", "spec", ".test.", ".spec." in the name).
|
|
366
412
|
Read the matching non-test files and check if any helpers can be reused in ${testFile}.
|
|
@@ -373,18 +419,18 @@ If helpers exist in util file that can be reused in ${testFile} without modifyin
|
|
|
373
419
|
|
|
374
420
|
## STEP 4: FIND LOCAL HELPERS IN OTHER SKYRAMP-GENERATED TEST FILES
|
|
375
421
|
|
|
376
|
-
You are looking for sibling test files generated by Skyramp's codegen. They start with the comment line \`${
|
|
422
|
+
You are looking for sibling test files generated by Skyramp's codegen. They start with the comment line \`${codegenMarker}…\` (note: the version suffix is what distinguishes them from the SkyrampUtils header). The substring \`${SKYRAMP_UTILS_HEADER}\` is contained in that line, so:
|
|
377
423
|
|
|
378
424
|
Use the Grep tool to search for other test files containing "${SKYRAMP_UTILS_HEADER}":
|
|
379
425
|
- Pattern: "${SKYRAMP_UTILS_HEADER}"
|
|
380
|
-
- Type: "
|
|
426
|
+
- Type: "${ext}"
|
|
381
427
|
- Output mode: "files_with_matches"
|
|
382
428
|
|
|
383
429
|
**CRITICAL: Exclude ${testFile} from the results** — only look at OTHER files, not the current file.
|
|
384
430
|
|
|
385
431
|
The result will mix two kinds of files:
|
|
386
432
|
- **Utils file(s)** — already handled by STEP 2/STEP 3.
|
|
387
|
-
- **Other Skyramp-generated test files** — these are the targets of STEP 4 / 4b / 5. Identify them by the \`${
|
|
433
|
+
- **Other Skyramp-generated test files** — these are the targets of STEP 4 / 4b / 5. Identify them by the \`${codegenMarker}\` codegen marker on line 1 (NOT the bare utils header).
|
|
388
434
|
|
|
389
435
|
**STOP HERE IF NO OTHER SKYRAMP-GENERATED TEST FILES FOUND**
|
|
390
436
|
**IF NO OTHER SKYRAMP-GENERATED TEST FILES ARE FOUND, SKIP TO STEP 6 - DO NOT CREATE ANY UTILS FILES.**
|
|
@@ -407,7 +453,7 @@ NOT A HELPER FUNCTION (do not extract):
|
|
|
407
453
|
|
|
408
454
|
## STEP 4b: PARAMETERIZE NEAR-DUPLICATE HELPERS ACROSS SKYRAMP-GENERATED TEST FILES
|
|
409
455
|
|
|
410
|
-
**This step handles the cross-test case: ${testFile} AND another Skyramp-generated test file each define their own helper that does the SAME shape of work with only literal-value differences. These should become ONE parameterized helper in \`${
|
|
456
|
+
**This step handles the cross-test case: ${testFile} AND another Skyramp-generated test file each define their own helper that does the SAME shape of work with only literal-value differences. These should become ONE parameterized helper in \`${utilsFile}\`.**
|
|
411
457
|
|
|
412
458
|
### Definition: "near-duplicate" helpers
|
|
413
459
|
|
|
@@ -478,9 +524,9 @@ For each helper defined locally in ${testFile}:
|
|
|
478
524
|
- **Body**: the common primitive sequence with the differing literals replaced by parameters.
|
|
479
525
|
- **Forbidden**: do NOT add if/else, ternaries, defaults, or any logic to bridge the differences. If a single body can't cover both call sites without new logic, they are NOT near-duplicates — leave them alone.
|
|
480
526
|
4. **If multiple matches** → process each pair independently. Merge every pair that clearly meets all three near-duplicate criteria. Skip any pair you're uncertain about — a missed merge is recoverable, a wrong merge breaks both tests.
|
|
481
|
-
5. **Write** the merged helper into \`${
|
|
527
|
+
5. **Write** the merged helper into \`${utilsFile}\` (create the file with the standard header if it doesn't exist; same header as STEP 5 below).
|
|
482
528
|
6. **Delete** both original helpers from their test files.
|
|
483
|
-
7. **Import** the merged helper from \`${
|
|
529
|
+
7. **Import** the merged helper from \`${utilsFile}\` into both test files.
|
|
484
530
|
8. **Replace** the two original call sites with calls to the merged helper, passing the values that were originally hardcoded or argument-passed in each test.
|
|
485
531
|
|
|
486
532
|
### Hard rules
|
|
@@ -488,7 +534,7 @@ For each helper defined locally in ${testFile}:
|
|
|
488
534
|
- **Conservative bias**: a wrong merge breaks both tests; a missed merge leaves a duplicate. When in doubt, do NOT merge.
|
|
489
535
|
- **No "cleanup"**: the merged body must mirror the original primitive sequence — same waits, same correlation patterns, same order. This is not refactoring.
|
|
490
536
|
- **Preserve typing style**: if originals used \`page: any\` (or untyped \`page\`), keep that convention.
|
|
491
|
-
- **Don't merge with helpers already in \`${
|
|
537
|
+
- **Don't merge with helpers already in \`${utilsFile}\`**: those are STEP 3's territory. STEP 4b is strictly cross-test merge between sibling Skyramp-generated test files.
|
|
492
538
|
|
|
493
539
|
---
|
|
494
540
|
|
|
@@ -505,15 +551,15 @@ For each helper defined locally in ${testFile}:
|
|
|
505
551
|
**IF ANY CONDITION IS NOT MET, SKIP TO STEP 6 - DO NOT CREATE ANY UTILS FILES.**
|
|
506
552
|
|
|
507
553
|
**CRITICAL:** For helpers found in STEP 4:
|
|
508
|
-
1. **CREATE** \`${
|
|
554
|
+
1. **CREATE** \`${utilsFile}\` file if it doesn't exist with the following header:
|
|
509
555
|
\`\`\`ts
|
|
510
|
-
${generateSkyrampHeader(
|
|
556
|
+
${generateSkyrampHeader(headerLang)}
|
|
511
557
|
\`\`\`
|
|
512
|
-
**CRITICAL: IF \`${
|
|
513
|
-
2. **COPY** those local helper functions from original test source files to \`${
|
|
558
|
+
**CRITICAL: IF \`${utilsFile}\` is already big, create a new file with a different name and add the header to the new file.**
|
|
559
|
+
2. **COPY** those local helper functions from original test source files to \`${utilsFile}\` without modifying them
|
|
514
560
|
3. **DELETE** those local helper functions from test source files (REMOVE THEM COMPLETELY) WITHOUT ANY OTHER CHANGES.
|
|
515
|
-
4. **IMPORT** those local helper functions from \`${
|
|
516
|
-
5. **IMPORT** those local helper functions from \`${
|
|
561
|
+
4. **IMPORT** those local helper functions from \`${utilsFile}\` back into test source files WITHOUT MAKING ANY OTHER CHANGE.
|
|
562
|
+
5. **IMPORT** those local helper functions from \`${utilsFile}\` into ${testFile}
|
|
517
563
|
6. **REPLACE** duplicate code in ${testFile} with calls to imported helper functions WITHOUT MAKING ANY OTHER CHANGE.
|
|
518
564
|
|
|
519
565
|
**CRITICAL: DO NOT CREATE UNNECESSARY HELPER FUNCTIONS**
|
|
@@ -525,14 +571,14 @@ For each helper defined locally in ${testFile}:
|
|
|
525
571
|
3. **VERIFY** that helper functions are NO LONGER in original test files
|
|
526
572
|
4. **VERIFY** that the original test files only have import statements and no duplicate code
|
|
527
573
|
5. **VERIFY** that both original and new test files import from utils and use the helper functions
|
|
528
|
-
5a. **VERIFY STEP 4b OUTCOMES** — for every near-duplicate pair you merged in STEP 4b: the merged helper exists in \`${
|
|
574
|
+
5a. **VERIFY STEP 4b OUTCOMES** — for every near-duplicate pair you merged in STEP 4b: the merged helper exists in \`${utilsFile}\` exactly once; BOTH original helpers are deleted from their respective test files; BOTH test files import the merged helper from \`${utilsFile}\`; BOTH original call sites are replaced with calls to the merged helper. If any of these is false, fix it before finishing.
|
|
529
575
|
6. **VERIFY** that no unnecessary helper functions were created
|
|
530
576
|
7. **VERIFY** that all helper functions in utils are actually imported and used in the test files
|
|
531
577
|
8. **REMOVE** any helper functions that are not being used after refactoring
|
|
532
578
|
9. **NEVER** refactor, reorganize, or restructure existing source test files beyond moving helpers
|
|
533
579
|
10. **Do NOT run the test** — your work is complete once the file is written
|
|
534
580
|
|
|
535
|
-
**Do NOT call skyramp_modularization.** Code reuse is complete. The test file has been refactored in place — no further tools should be called.
|
|
581
|
+
**Do NOT call skyramp_modularization.** Code reuse is complete. The test file has been refactored in place — no further tools should be called on this FALLBACK path. (If you took the POM-aware path instead, STEP 5's verification requirement still applies.)
|
|
536
582
|
|
|
537
583
|
SUMMARIZE THE CODE REUSE PROCESS AND THE RESULTS.
|
|
538
584
|
`;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { getPomAwareCodeReusePrompt } from "./pom-aware-code-reuse.js";
|
|
2
|
+
describe("getPomAwareCodeReusePrompt language derivation", () => {
|
|
3
|
+
it("uses .ts utils filename for typescript (default)", () => {
|
|
4
|
+
expect(getPomAwareCodeReusePrompt("t.spec.ts")).toContain("skyrampUtils.ts");
|
|
5
|
+
});
|
|
6
|
+
it("uses .js utils filename for javascript", () => {
|
|
7
|
+
const p = getPomAwareCodeReusePrompt("t.spec.js", "javascript");
|
|
8
|
+
expect(p).toContain("skyrampUtils.js");
|
|
9
|
+
expect(p).not.toContain("skyrampUtils.ts");
|
|
10
|
+
});
|
|
11
|
+
});
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { DraftedScenario } from "../../types/RepositoryAnalysis.js";
|
|
2
|
-
import { PriorityTier } from "../../types/TestRecommendation.js";
|
|
2
|
+
import { Novelty, PriorityTier } from "../../types/TestRecommendation.js";
|
|
3
3
|
/** "0" — Code Review: correctness analysis */
|
|
4
4
|
export declare const EXEC_STEP_CODE_REVIEW: string;
|
|
5
5
|
/** "1" — External test coverage verification */
|
|
@@ -10,10 +10,12 @@ export declare const EXEC_STEP_ENRICH: string;
|
|
|
10
10
|
export declare const EXEC_STEP_DIVERSITY: string;
|
|
11
11
|
/** "4" — Execute merged plan */
|
|
12
12
|
export declare const EXEC_STEP_EXECUTE: string;
|
|
13
|
+
/** "5" — Register test plan (SKYR-3879 Path B checkpoint) */
|
|
14
|
+
export declare const EXEC_STEP_REGISTER: string;
|
|
13
15
|
export declare function buildExecutionPlan(scored: Array<{
|
|
14
16
|
scenario: DraftedScenario;
|
|
15
17
|
priority: PriorityTier;
|
|
16
|
-
novelty:
|
|
18
|
+
novelty: Novelty;
|
|
17
19
|
}>, maxGen: number, topN: number, baseUrl: string, authHeaderValue: string, authSchemeSnippet: string, authTypeValue: string, seed: string, endpointCount: number, isUIOnlyPR: boolean, hasFrontendChanges?: boolean, hasTraces?: boolean, externalCoverage?: Set<string>, relevantExternalTestPaths?: string[],
|
|
18
20
|
/**
|
|
19
21
|
* Whether the diff classified at least one new/modified/removed endpoint.
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { roundRobinByType } from "../../recommendation/diversity.js";
|
|
1
2
|
import { AUTH_MIDDLEWARE_PATTERNS_STR } from "../../utils/workspaceAuth.js";
|
|
2
3
|
import { resolveServiceDetailsRef } from "../../utils/utils.js";
|
|
3
4
|
import { logger } from "../../utils/logger.js";
|
|
@@ -21,7 +22,7 @@ The highest-severity \`<bug_found>\` block from this code review triggers a mand
|
|
|
21
22
|
}
|
|
22
23
|
function _execCoverageBody(ctx) {
|
|
23
24
|
return `${ctx.externalTestFilesList}For every GENERATE item below, check its endpoint path and test type against the Existing Tests list (further down in the prompt).
|
|
24
|
-
- **\`[external]\` tests**: If the endpoint is already covered by an \`[external]\` test of the same type → skip the resource entirely (do NOT create or update),
|
|
25
|
+
- **\`[external]\` tests**: If the endpoint is already covered by an \`[external]\` test of the same type **that exercises the behavior this PR changes** → skip the resource entirely (do NOT create or update). Resource-level overlap alone is NOT coverage: when the PR tightens or adds a constraint (e.g. a max-items/max-length/enum bound), an external test that never crosses the new bound does not cover it — generate the boundary test. This matters most for single-resource APIs (e.g. one CRD served by the kube-apiserver), where resource-level dedup would permanently block all generation. \`bug_caught\` and attack-surface \`security_boundary\` items get the strictest reading: they may be skipped only if the external test directly asserts the same flaw/bypass and would fail/pass based on that same bug. Backfill from ADDITIONAL using the priority order below:
|
|
25
26
|
1. **BUG-CATCHING TESTS FIRST (CRITICAL)**: If source code analysis revealed a bug, logic error, or incorrect formula (e.g. discount math adding instead of subtracting, off-by-one errors, missing validation), CREATE A TEST THAT EXPOSES IT. The test SHOULD FAIL — that's the point. Document the bug. Example: if discount formula is wrong, test with discount=20% and assert correct math. If no bug found, skip to #2.
|
|
26
27
|
2. **PR-endpoint edge cases**: Look for integration test candidates covering error paths, boundary values, or alternative scenarios for the SAME endpoints changed in the PR diff. If no suitable candidate exists in ADDITIONAL, derive one from your source-code enrichment findings.
|
|
27
28
|
3. **Same-resource other scenarios**: Other HTTP methods or flows on the same resource group touched by the PR.
|
|
@@ -94,6 +95,11 @@ For each pair of GENERATE items, ask: same HTTP method + path + step sequence +
|
|
|
94
95
|
|
|
95
96
|
Same step sequence with only payload differences (e.g. 10% vs 5% discount both returning 200) = same code path = duplicate. Different scenario names do not make duplicate tests distinct.`;
|
|
96
97
|
}
|
|
98
|
+
function _execRegisterBody(_ctx) {
|
|
99
|
+
return `Register your complete candidate list — every test you would generate OR recommend — via \`skyramp_register_test_plan\` (\`stateFile\` required). Include a discriminator claim (\`discriminator\` field — valid kinds and anchor rules are in the tool schema) for candidates probing the changed logic identified in Step ${EXEC_STEP_CODE_REVIEW}/Step ${EXEC_STEP_ENRICH}.
|
|
100
|
+
|
|
101
|
+
The returned GENERATE list is mandatory and final — generation tools reject unregistered scenarios. If the tool demotes a discriminator claim (returned in \`demotions\` with a reason), either strengthen the claim — a step that actually exercises the declared \`kind\`, or a verbatim anchor that occurs in the diff — or drop it; the candidate itself stays in the plan either way.`;
|
|
102
|
+
}
|
|
97
103
|
function _execExecuteBody(ctx) {
|
|
98
104
|
return `Replace any scenario that pairs unrelated resources with one reflecting actual foreign-key relationships in the codebase.
|
|
99
105
|
Use the field names and values from the \`<source_evidence>\` blocks you quoted in Step ${EXEC_STEP_ENRICH} to fill all tool call parameters. Prefer reusing Step ${EXEC_STEP_ENRICH} evidence when it already resolves a placeholder, but if a placeholder cannot be replaced with concrete values from files already read, you may read the specific schema, model, or handler file needed to resolve it. Assert response field values, not just status codes.
|
|
@@ -127,6 +133,7 @@ const _execPlan = new PromptPlan({ startFrom: 0 })
|
|
|
127
133
|
.step("ENRICH", "Parameter Grounding & Priority Assignment", _execEnrichBody)
|
|
128
134
|
.step("DIVERSITY", (ctx) => `Diversity check (using enriched knowledge from Step ${ctx.enrichStepLabel})`, _execDiversityBody)
|
|
129
135
|
.step("EXECUTE", "Execute merged plan in rank order", _execExecuteBody)
|
|
136
|
+
.step("REGISTER", "Register your test plan", _execRegisterBody)
|
|
130
137
|
.done();
|
|
131
138
|
// ── Exported step label constants ─────────────────────────────────────────────
|
|
132
139
|
/** "0" — Code Review: correctness analysis */
|
|
@@ -139,6 +146,8 @@ export const EXEC_STEP_ENRICH = _execPlan.labels.ENRICH; // "2"
|
|
|
139
146
|
export const EXEC_STEP_DIVERSITY = _execPlan.labels.DIVERSITY; // "3"
|
|
140
147
|
/** "4" — Execute merged plan */
|
|
141
148
|
export const EXEC_STEP_EXECUTE = _execPlan.labels.EXECUTE; // "4"
|
|
149
|
+
/** "5" — Register test plan (SKYR-3879 Path B checkpoint) */
|
|
150
|
+
export const EXEC_STEP_REGISTER = _execPlan.labels.REGISTER; // "5"
|
|
142
151
|
const SERVICE_REFS = resolveServiceDetailsRef();
|
|
143
152
|
function prioritizeAttackSurfaceBundles(items) {
|
|
144
153
|
const reordered = [];
|
|
@@ -154,69 +163,6 @@ function prioritizeAttackSurfaceBundles(items) {
|
|
|
154
163
|
}
|
|
155
164
|
return reordered;
|
|
156
165
|
}
|
|
157
|
-
/**
|
|
158
|
-
* Select `count` items from a rank-ordered list, distributing GENERATE slots
|
|
159
|
-
* EVENLY across the test types present, with spillover.
|
|
160
|
-
*
|
|
161
|
-
* Policy (kept identical to the multi-repo prose in testbot-prompts.ts's
|
|
162
|
-
* "Cross-repo test generation" block — change both together):
|
|
163
|
-
* - Protected items first: CRITICAL-priority and attack-surface security_boundary
|
|
164
|
-
* scenarios always take a slot before round-robin (they must stay in GENERATE).
|
|
165
|
-
* - Bucket the rest by inferred test type (contract vs integration — the same
|
|
166
|
-
* inference used when rendering: `testType ?? (steps===1 ? contract : integration)`).
|
|
167
|
-
* - Round-robin one item per non-empty bucket per round, in the buckets' order of
|
|
168
|
-
* first appearance in the rank-ordered list (so the highest-ranked type wins
|
|
169
|
-
* round 1), preserving rank order within each bucket.
|
|
170
|
-
* - Spillover: an exhausted bucket is skipped on later rounds, so its freed slots
|
|
171
|
-
* go to the next type's next-highest item.
|
|
172
|
-
*
|
|
173
|
-
* Degenerate cases match the previous pure rank-order slice exactly: a single type
|
|
174
|
-
* present, or `count >= items.length`, returns the same items in the same order —
|
|
175
|
-
* so backend-only / single-type runs are unchanged (no regression).
|
|
176
|
-
*/
|
|
177
|
-
function roundRobinByType(rankOrdered, count) {
|
|
178
|
-
if (count <= 0)
|
|
179
|
-
return [];
|
|
180
|
-
// Everything fits → no need to bucket; identical to the old slice.
|
|
181
|
-
if (count >= rankOrdered.length)
|
|
182
|
-
return rankOrdered.slice(0, count);
|
|
183
|
-
const inferType = (s) => s.testType ?? (s.steps.length === 1 ? "contract" : "integration");
|
|
184
|
-
// Protected items occupy GENERATE slots first, in rank order.
|
|
185
|
-
const selected = [];
|
|
186
|
-
const remaining = [];
|
|
187
|
-
for (const item of rankOrdered) {
|
|
188
|
-
if (selected.length < count &&
|
|
189
|
-
(item.priority === "CRITICAL" || isAttackSurfaceSecurityBoundary(item.scenario))) {
|
|
190
|
-
selected.push(item);
|
|
191
|
-
}
|
|
192
|
-
else {
|
|
193
|
-
remaining.push(item);
|
|
194
|
-
}
|
|
195
|
-
}
|
|
196
|
-
// Bucket the remainder by inferred type, preserving rank order and first-appearance
|
|
197
|
-
// bucket order.
|
|
198
|
-
const order = [];
|
|
199
|
-
const buckets = new Map();
|
|
200
|
-
for (const item of remaining) {
|
|
201
|
-
const t = inferType(item.scenario);
|
|
202
|
-
if (!buckets.has(t)) {
|
|
203
|
-
buckets.set(t, []);
|
|
204
|
-
order.push(t);
|
|
205
|
-
}
|
|
206
|
-
buckets.get(t).push(item);
|
|
207
|
-
}
|
|
208
|
-
// Round-robin one per non-empty bucket per round until full.
|
|
209
|
-
while (selected.length < count && order.some((t) => buckets.get(t).length > 0)) {
|
|
210
|
-
for (const t of order) {
|
|
211
|
-
if (selected.length >= count)
|
|
212
|
-
break;
|
|
213
|
-
const bucket = buckets.get(t);
|
|
214
|
-
if (bucket.length > 0)
|
|
215
|
-
selected.push(bucket.shift());
|
|
216
|
-
}
|
|
217
|
-
}
|
|
218
|
-
return selected;
|
|
219
|
-
}
|
|
220
166
|
export function buildExecutionPlan(scored, maxGen, topN, baseUrl, authHeaderValue, authSchemeSnippet, authTypeValue, seed, endpointCount, isUIOnlyPR, hasFrontendChanges = false, hasTraces = false, externalCoverage = new Set(), relevantExternalTestPaths = [],
|
|
221
167
|
/**
|
|
222
168
|
* Whether the diff classified at least one new/modified/removed endpoint.
|
|
@@ -473,7 +419,7 @@ ${buildScopeAssessmentSection(topN, maxGen, isUIOnlyPR, isUIOnlyPR ? 100 : hasFr
|
|
|
473
419
|
|
|
474
420
|
${_execPlan.render(_ctx)}
|
|
475
421
|
|
|
476
|
-
### GENERATE (after completing Steps ${EXEC_STEP_CODE_REVIEW}–${EXEC_STEP_EXECUTE} above) —
|
|
422
|
+
### GENERATE (after completing Steps ${EXEC_STEP_CODE_REVIEW}–${EXEC_STEP_EXECUTE} above and registering via Step ${EXEC_STEP_REGISTER}) — the list below is a starting point; \`skyramp_register_test_plan\`'s returned GENERATE list is the final, mandatory one. Generate exactly those items in order; add variations to ADDITIONAL instead. If Step ${EXEC_STEP_COVERAGE} converts an item to UPDATE, backfill from ADDITIONAL (priority order in Step ${EXEC_STEP_COVERAGE})
|
|
477
423
|
|
|
478
424
|
${isUIOnlyPR
|
|
479
425
|
? uiGenerateBlocks ||
|