@skyramp/mcp 0.3.1 → 0.3.2-rc.pom-2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/build/index.js +2 -1
- package/build/prompts/code-reuse.d.ts +7 -1
- package/build/prompts/code-reuse.js +8 -5
- package/build/prompts/code-reuse.test.d.ts +1 -0
- package/build/prompts/code-reuse.test.js +62 -0
- package/build/prompts/pom-aware-code-reuse.d.ts +6 -1
- package/build/prompts/pom-aware-code-reuse.js +100 -53
- package/build/prompts/pom-aware-code-reuse.test.d.ts +1 -0
- package/build/prompts/pom-aware-code-reuse.test.js +11 -0
- package/build/prompts/test-recommendation/diffExecutionPlan.d.ts +4 -2
- package/build/prompts/test-recommendation/diffExecutionPlan.js +11 -65
- package/build/prompts/test-recommendation/fullRepoCatalog.d.ts +2 -2
- package/build/prompts/test-recommendation/recommendationSections.js +5 -2
- package/build/prompts/test-recommendation/scopeAssessment.js +1 -1
- package/build/prompts/test-recommendation/test-recommendation-prompt.d.ts +26 -1
- package/build/prompts/test-recommendation/test-recommendation-prompt.js +68 -56
- package/build/prompts/test-recommendation/test-recommendation-prompt.test.js +48 -11
- package/build/prompts/testbot/testbot-prompts.d.ts +1 -1
- package/build/prompts/testbot/testbot-prompts.js +67 -21
- package/build/prompts/testbot/testbot-prompts.test.js +44 -0
- package/build/recommendation/budgeters/diversityBalancedBudgeter.d.ts +7 -0
- package/build/recommendation/budgeters/diversityBalancedBudgeter.js +71 -0
- package/build/recommendation/budgeters/diversityBalancedBudgeter.test.d.ts +1 -0
- package/build/recommendation/budgeters/diversityBalancedBudgeter.test.js +75 -0
- package/build/recommendation/budgeters/fixedNBudgeter.d.ts +7 -0
- package/build/recommendation/budgeters/fixedNBudgeter.js +11 -0
- package/build/recommendation/budgeters/fixedNBudgeter.test.d.ts +1 -0
- package/build/recommendation/budgeters/fixedNBudgeter.test.js +66 -0
- package/build/recommendation/budgeters/shared.d.ts +19 -0
- package/build/recommendation/budgeters/shared.js +66 -0
- package/build/recommendation/discriminators.d.ts +31 -0
- package/build/recommendation/discriminators.js +355 -0
- package/build/recommendation/discriminators.test.d.ts +1 -0
- package/build/recommendation/discriminators.test.js +324 -0
- package/build/recommendation/diversity.d.ts +47 -0
- package/build/recommendation/diversity.js +101 -0
- package/build/recommendation/diversity.test.d.ts +1 -0
- package/build/recommendation/diversity.test.js +77 -0
- package/build/recommendation/planRanker.d.ts +50 -0
- package/build/recommendation/planRanker.js +67 -0
- package/build/recommendation/planRanker.test.d.ts +1 -0
- package/build/recommendation/planRanker.test.js +110 -0
- package/build/recommendation/testFixtures.d.ts +25 -0
- package/build/recommendation/testFixtures.js +45 -0
- package/build/resources/testbotResource.js +4 -1
- package/build/services/ScenarioGenerationService.d.ts +5 -0
- package/build/services/ScenarioGenerationService.js +16 -1
- package/build/services/ScenarioGenerationService.test.js +44 -0
- package/build/services/TestExecutionService.d.ts +15 -1
- package/build/services/TestExecutionService.js +210 -55
- package/build/services/TestExecutionService.test.js +397 -0
- package/build/services/TestGenerationService.js +19 -1
- package/build/services/TestGenerationService.test.js +58 -0
- package/build/tool-phases.js +1 -0
- package/build/toolNames.d.ts +19 -0
- package/build/toolNames.js +19 -0
- package/build/tools/code-refactor/codeReuseTool.d.ts +7 -0
- package/build/tools/code-refactor/codeReuseTool.js +130 -4
- package/build/tools/code-refactor/codeReuseTool.test.d.ts +1 -0
- package/build/tools/code-refactor/codeReuseTool.test.js +290 -0
- package/build/tools/executeSkyrampTestTool.js +8 -2
- package/build/tools/generate-tests/generateBatchScenarioRestTool.d.ts +6 -1
- package/build/tools/generate-tests/generateBatchScenarioRestTool.js +110 -17
- package/build/tools/generate-tests/generateBatchScenarioRestTool.test.js +147 -0
- package/build/tools/generate-tests/generateContractRestTool.js +11 -1
- package/build/tools/generate-tests/generateIntegrationRestTool.js +24 -1
- package/build/tools/generate-tests/generateIntegrationRestTool.test.d.ts +1 -0
- package/build/tools/generate-tests/generateIntegrationRestTool.test.js +159 -0
- package/build/tools/generate-tests/planGuard.d.ts +13 -0
- package/build/tools/generate-tests/planGuard.js +78 -0
- package/build/tools/generate-tests/planGuard.test.d.ts +1 -0
- package/build/tools/generate-tests/planGuard.test.js +185 -0
- package/build/tools/generate-tests/scenarioFileIdentity.d.ts +10 -0
- package/build/tools/generate-tests/scenarioFileIdentity.js +46 -0
- package/build/tools/generate-tests/scenarioLint.d.ts +30 -0
- package/build/tools/generate-tests/scenarioLint.js +150 -0
- package/build/tools/generate-tests/scenarioLint.test.d.ts +1 -0
- package/build/tools/generate-tests/scenarioLint.test.js +100 -0
- package/build/tools/submitReportTool.js +78 -0
- package/build/tools/submitReportTool.test.js +255 -0
- package/build/tools/test-management/analyzeChangesTool.js +55 -2
- package/build/tools/test-management/analyzeChangesTool.test.js +12 -0
- package/build/tools/test-management/index.d.ts +1 -0
- package/build/tools/test-management/index.js +1 -0
- package/build/tools/test-management/registerTestPlanTool.d.ts +2 -0
- package/build/tools/test-management/registerTestPlanTool.js +329 -0
- package/build/tools/test-management/registerTestPlanTool.test.d.ts +1 -0
- package/build/tools/test-management/registerTestPlanTool.test.js +296 -0
- package/build/types/Recommendation.d.ts +97 -0
- package/build/types/Recommendation.js +48 -0
- package/build/types/RepositoryAnalysis.d.ts +14 -14
- package/build/types/TestExecution.d.ts +2 -0
- package/build/types/TestRecommendation.d.ts +12 -1
- package/build/types/TestRecommendation.js +26 -11
- package/build/types/TestTypes.js +1 -1
- package/build/utils/AnalysisStateManager.d.ts +47 -0
- package/build/utils/docker.test.js +1 -1
- package/build/utils/planMatchKeys.d.ts +61 -0
- package/build/utils/planMatchKeys.js +125 -0
- package/build/utils/pom-scope/import-expansion.d.ts +5 -0
- package/build/utils/pom-scope/import-expansion.js +32 -0
- package/build/utils/pom-scope/index.d.ts +39 -0
- package/build/utils/pom-scope/index.js +120 -0
- package/build/utils/pom-scope/index.test.d.ts +1 -0
- package/build/utils/pom-scope/index.test.js +239 -0
- package/build/utils/pom-scope/pom-files.d.ts +3 -0
- package/build/utils/pom-scope/pom-files.js +48 -0
- package/build/utils/pom-scope/pom-files.test.d.ts +1 -0
- package/build/utils/pom-scope/pom-files.test.js +29 -0
- package/build/utils/pom-scope/scoring.d.ts +20 -0
- package/build/utils/pom-scope/scoring.js +45 -0
- package/build/utils/pom-scope/scoring.test.d.ts +1 -0
- package/build/utils/pom-scope/scoring.test.js +39 -0
- package/build/utils/pom-scope/selector-extractor.d.ts +7 -0
- package/build/utils/pom-scope/selector-extractor.js +57 -0
- package/build/utils/pom-scope/selector-extractor.test.d.ts +1 -0
- package/build/utils/pom-scope/selector-extractor.test.js +67 -0
- package/build/utils/pom-verify/__fixtures__/af-style/asset-list.page.d.ts +5 -0
- package/build/utils/pom-verify/__fixtures__/af-style/asset-list.page.js +5 -0
- package/build/utils/pom-verify/__fixtures__/af-style/pageobjects/asset-list-page.d.ts +5 -0
- package/build/utils/pom-verify/__fixtures__/af-style/pageobjects/asset-list-page.js +9 -0
- package/build/utils/pom-verify/__fixtures__/af-style/report.iframe.page.d.ts +4 -0
- package/build/utils/pom-verify/__fixtures__/af-style/report.iframe.page.js +4 -0
- package/build/utils/pom-verify/__fixtures__/af-style/workflow-footer.page.d.ts +4 -0
- package/build/utils/pom-verify/__fixtures__/af-style/workflow-footer.page.js +4 -0
- package/build/utils/pom-verify/bindings.d.ts +19 -0
- package/build/utils/pom-verify/bindings.js +161 -0
- package/build/utils/pom-verify/bindings.test.d.ts +1 -0
- package/build/utils/pom-verify/bindings.test.js +164 -0
- package/build/utils/pom-verify/calls.d.ts +16 -0
- package/build/utils/pom-verify/calls.js +42 -0
- package/build/utils/pom-verify/calls.test.d.ts +1 -0
- package/build/utils/pom-verify/calls.test.js +61 -0
- package/build/utils/pom-verify/index.d.ts +4 -0
- package/build/utils/pom-verify/index.js +4 -0
- package/build/utils/pom-verify/resolve.d.ts +7 -0
- package/build/utils/pom-verify/resolve.js +27 -0
- package/build/utils/pom-verify/resolve.test.d.ts +1 -0
- package/build/utils/pom-verify/resolve.test.js +68 -0
- package/build/utils/pom-verify/strip.d.ts +9 -0
- package/build/utils/pom-verify/strip.js +89 -0
- package/build/utils/pom-verify/verify.d.ts +14 -0
- package/build/utils/pom-verify/verify.js +158 -0
- package/build/utils/pom-verify/verify.test.d.ts +1 -0
- package/build/utils/pom-verify/verify.test.js +325 -0
- package/build/utils/reportVerification.d.ts +61 -0
- package/build/utils/reportVerification.js +104 -0
- package/build/utils/reportVerification.test.d.ts +1 -0
- package/build/utils/reportVerification.test.js +185 -0
- package/build/utils/scenarioDrafting.js +5 -5
- package/build/utils/versions.d.ts +3 -3
- package/build/utils/versions.js +1 -1
- package/build/utils/workspaceAuth.d.ts +9 -1
- package/build/utils/workspaceAuth.js +25 -5
- package/build/utils/workspaceAuth.test.js +48 -0
- package/build/workspace/workspace.d.ts +20 -0
- package/build/workspace/workspace.js +4 -0
- package/build/workspace/workspace.test.js +10 -0
- package/node_modules/playwright/lib/mcp/skyramp/traceRecordingBackend.js +77 -8
- package/node_modules/playwright/node_modules/playwright-core/lib/generated/injectedScriptSource.js +1 -1
- package/node_modules/playwright/node_modules/playwright-core/lib/generated/pollingRecorderSource.js +1 -1
- package/node_modules/playwright/node_modules/playwright-core/lib/utils/isomorphic/volatileDate.js +101 -0
- package/node_modules/playwright/node_modules/playwright-core/lib/utils.js +2 -0
- package/node_modules/playwright/node_modules/playwright-core/lib/vite/traceViewer/assets/{codeMirrorModule-aszq5EdG.js → codeMirrorModule-Bzd72-bG.js} +1 -1
- package/node_modules/playwright/node_modules/playwright-core/lib/vite/traceViewer/assets/{defaultSettingsView-BxS7Jm4s.js → defaultSettingsView-DzxTioTK.js} +101 -101
- package/node_modules/playwright/node_modules/playwright-core/lib/vite/traceViewer/{index.D4JTTy4R.js → index.BGc30U3S.js} +1 -1
- package/node_modules/playwright/node_modules/playwright-core/lib/vite/traceViewer/index.html +2 -2
- package/node_modules/playwright/node_modules/playwright-core/lib/vite/traceViewer/{uiMode.DaRMQKOI.js → uiMode.IaDrb29A.js} +1 -1
- package/node_modules/playwright/node_modules/playwright-core/lib/vite/traceViewer/uiMode.html +2 -2
- package/node_modules/playwright/node_modules/playwright-core/package.json +1 -1
- package/node_modules/playwright/node_modules/playwright-core/src/generated/injectedScriptSource.ts +1 -1
- package/node_modules/playwright/node_modules/playwright-core/src/generated/pollingRecorderSource.ts +1 -1
- package/node_modules/playwright/node_modules/playwright-core/src/utils/isomorphic/volatileDate.ts +131 -0
- package/node_modules/playwright/node_modules/playwright-core/src/utils.ts +1 -0
- package/node_modules/playwright/package.json +1 -1
- package/package.json +3 -3
- package/node_modules/playwright/node_modules/playwright-core/.DS_Store +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { ScenarioSource } from "../types/RepositoryAnalysis.js";
|
|
2
2
|
import { TestType } from "../types/TestTypes.js";
|
|
3
|
-
import { CATEGORY_PRIORITY } from "../types/TestRecommendation.js";
|
|
3
|
+
import { CATEGORY_PRIORITY, PriorityTier } from "../types/TestRecommendation.js";
|
|
4
4
|
import { inferExpectedStatus } from "./httpDefaults.js";
|
|
5
5
|
import { WorkspaceAuthType } from "./workspaceAuth.js";
|
|
6
6
|
import { deriveResourceToken } from "./importerHop.js";
|
|
@@ -229,8 +229,8 @@ const TIER_ORDER = { CRITICAL: 4, HIGH: 3, MEDIUM: 2, LOW: 1 };
|
|
|
229
229
|
export function capScenarios(scenarios) {
|
|
230
230
|
if (scenarios.length <= MAX_TOTAL_SCENARIOS)
|
|
231
231
|
return scenarios;
|
|
232
|
-
const critical = scenarios.filter(s => CATEGORY_PRIORITY[s.category] ===
|
|
233
|
-
const rest = scenarios.filter(s => CATEGORY_PRIORITY[s.category] !==
|
|
232
|
+
const critical = scenarios.filter(s => CATEGORY_PRIORITY[s.category] === PriorityTier.CRITICAL);
|
|
233
|
+
const rest = scenarios.filter(s => CATEGORY_PRIORITY[s.category] !== PriorityTier.CRITICAL);
|
|
234
234
|
const breadthPicks = [];
|
|
235
235
|
const seenCategories = new Set();
|
|
236
236
|
for (const s of rest) {
|
|
@@ -242,8 +242,8 @@ export function capScenarios(scenarios) {
|
|
|
242
242
|
const breadthSet = new Set(breadthPicks);
|
|
243
243
|
const remaining = rest.filter(s => !breadthSet.has(s));
|
|
244
244
|
remaining.sort((a, b) => {
|
|
245
|
-
const ta = TIER_ORDER[CATEGORY_PRIORITY[a.category] ??
|
|
246
|
-
const tb = TIER_ORDER[CATEGORY_PRIORITY[b.category] ??
|
|
245
|
+
const ta = TIER_ORDER[CATEGORY_PRIORITY[a.category] ?? PriorityTier.LOW] ?? 1;
|
|
246
|
+
const tb = TIER_ORDER[CATEGORY_PRIORITY[b.category] ?? PriorityTier.LOW] ?? 1;
|
|
247
247
|
return tb - ta;
|
|
248
248
|
});
|
|
249
249
|
const budget = MAX_TOTAL_SCENARIOS - critical.length - breadthPicks.length;
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
export declare const SKYRAMP_IMAGE_VERSION = "v1.3.
|
|
2
|
-
export declare const EXECUTOR_DOCKER_IMAGE = "skyramp/executor:v1.3.
|
|
3
|
-
export declare const WORKER_DOCKER_IMAGE = "skyramp/worker:v1.3.
|
|
1
|
+
export declare const SKYRAMP_IMAGE_VERSION = "v1.3.35";
|
|
2
|
+
export declare const EXECUTOR_DOCKER_IMAGE = "skyramp/executor:v1.3.35";
|
|
3
|
+
export declare const WORKER_DOCKER_IMAGE = "skyramp/worker:v1.3.35";
|
package/build/utils/versions.js
CHANGED
|
@@ -40,8 +40,13 @@ export interface WorkspaceBaseUrlResult {
|
|
|
40
40
|
*
|
|
41
41
|
* e.g. repositoryPath=/repo, testDirectory=backend/tests, testFile=/repo/backend/tests/test_api.py
|
|
42
42
|
* → { baseUrl: "http://localhost:8000", candidates: [] }
|
|
43
|
+
*
|
|
44
|
+
* `testRepoPath` (SKYR-3819): in a cross-repo run, delivered test files live under
|
|
45
|
+
* the run's testsRepoDir but mirror the service's testDirectory structure. When
|
|
46
|
+
* provided, each service's testDirectory is also resolved against this root so
|
|
47
|
+
* cross-repo test files still resolve the SUT service's baseUrl and dockerNetwork.
|
|
43
48
|
*/
|
|
44
|
-
export declare function getWorkspaceBaseUrl(repositoryPath: string, testFile?: string, language?: string): Promise<WorkspaceBaseUrlResult>;
|
|
49
|
+
export declare function getWorkspaceBaseUrl(repositoryPath: string, testFile?: string, language?: string, testRepoPath?: string): Promise<WorkspaceBaseUrlResult>;
|
|
45
50
|
/**
|
|
46
51
|
* Known auth-related HTTP header names (lowercase).
|
|
47
52
|
* Single source of truth used for both:
|
|
@@ -125,3 +130,6 @@ export interface WorkspaceAuthConfig {
|
|
|
125
130
|
authScheme?: string;
|
|
126
131
|
}
|
|
127
132
|
export declare function getWorkspaceAuthConfig(repositoryPath: string): Promise<WorkspaceAuthConfig>;
|
|
133
|
+
/** True when any workspace service declares api.skipTLSVerify — the SUT serves
|
|
134
|
+
* self-signed TLS and generated tests must skip cert verification (SKYR-3961). */
|
|
135
|
+
export declare function getWorkspaceSkipTLSVerify(repositoryPath: string): Promise<boolean>;
|
|
@@ -56,8 +56,13 @@ export async function readWorkspaceConfigRaw(workspacePath) {
|
|
|
56
56
|
*
|
|
57
57
|
* e.g. repositoryPath=/repo, testDirectory=backend/tests, testFile=/repo/backend/tests/test_api.py
|
|
58
58
|
* → { baseUrl: "http://localhost:8000", candidates: [] }
|
|
59
|
+
*
|
|
60
|
+
* `testRepoPath` (SKYR-3819): in a cross-repo run, delivered test files live under
|
|
61
|
+
* the run's testsRepoDir but mirror the service's testDirectory structure. When
|
|
62
|
+
* provided, each service's testDirectory is also resolved against this root so
|
|
63
|
+
* cross-repo test files still resolve the SUT service's baseUrl and dockerNetwork.
|
|
59
64
|
*/
|
|
60
|
-
export async function getWorkspaceBaseUrl(repositoryPath, testFile, language) {
|
|
65
|
+
export async function getWorkspaceBaseUrl(repositoryPath, testFile, language, testRepoPath) {
|
|
61
66
|
const noMatch = { baseUrl: undefined, dockerNetwork: undefined, candidates: [] };
|
|
62
67
|
if (!testFile)
|
|
63
68
|
return noMatch;
|
|
@@ -67,15 +72,22 @@ export async function getWorkspaceBaseUrl(repositoryPath, testFile, language) {
|
|
|
67
72
|
return noMatch;
|
|
68
73
|
const services = rawConfig.services ?? [];
|
|
69
74
|
// Normalise testFile to an absolute path for reliable prefix matching
|
|
75
|
+
// (path.resolve also collapses any ".." segments in an absolute input)
|
|
70
76
|
const absTestFile = path.isAbsolute(testFile)
|
|
71
|
-
? testFile
|
|
77
|
+
? path.resolve(testFile)
|
|
72
78
|
: path.resolve(repositoryPath, testFile);
|
|
73
|
-
// Step 1: filter by testDirectory prefix
|
|
79
|
+
// Step 1: filter by testDirectory prefix — resolved against the SUT repo,
|
|
80
|
+
// and additionally against the cross-repo test root when one is known.
|
|
81
|
+
const testDirRoots = [repositoryPath];
|
|
82
|
+
if (testRepoPath)
|
|
83
|
+
testDirRoots.push(testRepoPath);
|
|
74
84
|
let matches = services.filter((s) => {
|
|
75
85
|
if (!s.testDirectory || !s.api?.baseUrl)
|
|
76
86
|
return false;
|
|
77
|
-
|
|
78
|
-
|
|
87
|
+
return testDirRoots.some((root) => {
|
|
88
|
+
const absTestDirectory = path.resolve(root, s.testDirectory);
|
|
89
|
+
return absTestFile.startsWith(absTestDirectory + path.sep) || absTestFile === absTestDirectory;
|
|
90
|
+
});
|
|
79
91
|
});
|
|
80
92
|
// Step 2: narrow by language if still ambiguous
|
|
81
93
|
if (matches.length > 1 && language) {
|
|
@@ -330,3 +342,11 @@ export async function getWorkspaceAuthConfig(repositoryPath) {
|
|
|
330
342
|
const authScheme = svc?.api?.authScheme;
|
|
331
343
|
return { authHeader, authType, authScheme };
|
|
332
344
|
}
|
|
345
|
+
/** True when any workspace service declares api.skipTLSVerify — the SUT serves
|
|
346
|
+
* self-signed TLS and generated tests must skip cert verification (SKYR-3961). */
|
|
347
|
+
export async function getWorkspaceSkipTLSVerify(repositoryPath) {
|
|
348
|
+
const rawConfig = await readWorkspaceConfigRaw(repositoryPath);
|
|
349
|
+
if (!rawConfig)
|
|
350
|
+
return false;
|
|
351
|
+
return (rawConfig.services ?? []).some((s) => s.api?.skipTLSVerify === true);
|
|
352
|
+
}
|
|
@@ -210,3 +210,51 @@ describe("getWorkspaceBaseUrl", () => {
|
|
|
210
210
|
expect(result.baseUrl).toBeUndefined();
|
|
211
211
|
});
|
|
212
212
|
});
|
|
213
|
+
// SKYR-3819: in a cross-repo run, delivered test files live under testsRepoDir
|
|
214
|
+
// (a sibling of the SUT repo) but mirror the service's testDirectory structure.
|
|
215
|
+
// getWorkspaceBaseUrl must also match testDirectory against the test-repo root,
|
|
216
|
+
// or cross-repo executions lose SKYRAMP_TEST_BASE_URL and dockerNetwork.
|
|
217
|
+
describe("getWorkspaceBaseUrl with testRepoPath (cross-repo)", () => {
|
|
218
|
+
const TEST_REPO = "/some/test-repo";
|
|
219
|
+
it("matches a cross-repo test file against testDirectory resolved from testRepoPath", async () => {
|
|
220
|
+
const result = await getWorkspaceBaseUrl(REPO, `${TEST_REPO}/backend/tests/orders_test.py`, "python", TEST_REPO);
|
|
221
|
+
expect(result.baseUrl).toBe("http://localhost:8000");
|
|
222
|
+
expect(result.candidates).toHaveLength(0);
|
|
223
|
+
});
|
|
224
|
+
it("returns dockerNetwork for a cross-repo match", async () => {
|
|
225
|
+
const { WorkspaceConfigManager } = await import("../workspace/workspace.js");
|
|
226
|
+
WorkspaceConfigManager.mockImplementationOnce(() => ({
|
|
227
|
+
exists: jest.fn().mockResolvedValue(true),
|
|
228
|
+
read: jest.fn().mockResolvedValue({
|
|
229
|
+
services: [
|
|
230
|
+
{
|
|
231
|
+
serviceName: "backend",
|
|
232
|
+
testDirectory: "tests",
|
|
233
|
+
api: { baseUrl: "http://localhost:8000" },
|
|
234
|
+
runtimeDetails: { dockerNetwork: "myapp_default" },
|
|
235
|
+
},
|
|
236
|
+
],
|
|
237
|
+
}),
|
|
238
|
+
}));
|
|
239
|
+
const result = await getWorkspaceBaseUrl(REPO, `${TEST_REPO}/tests/orders_test.py`, undefined, TEST_REPO);
|
|
240
|
+
expect(result.baseUrl).toBe("http://localhost:8000");
|
|
241
|
+
expect(result.dockerNetwork).toBe("myapp_default");
|
|
242
|
+
});
|
|
243
|
+
it("still matches SUT-repo test files when testRepoPath is provided", async () => {
|
|
244
|
+
const result = await getWorkspaceBaseUrl(REPO, `${REPO}/backend/tests/orders_test.py`, "python", TEST_REPO);
|
|
245
|
+
expect(result.baseUrl).toBe("http://localhost:8000");
|
|
246
|
+
});
|
|
247
|
+
it("does not match a cross-repo file that is outside every testDirectory", async () => {
|
|
248
|
+
const result = await getWorkspaceBaseUrl(REPO, `${TEST_REPO}/unrelated/orders_test.py`, "python", TEST_REPO);
|
|
249
|
+
expect(result.baseUrl).toBeUndefined();
|
|
250
|
+
expect(result.candidates).toHaveLength(0);
|
|
251
|
+
});
|
|
252
|
+
it("does not match a cross-repo file without testRepoPath (pre-existing behavior)", async () => {
|
|
253
|
+
const result = await getWorkspaceBaseUrl(REPO, `${TEST_REPO}/backend/tests/orders_test.py`, "python");
|
|
254
|
+
expect(result.baseUrl).toBeUndefined();
|
|
255
|
+
});
|
|
256
|
+
it("normalizes '..' segments in an absolute testFile before prefix matching", async () => {
|
|
257
|
+
const result = await getWorkspaceBaseUrl(REPO, `${TEST_REPO}/somewhere/../backend/tests/orders_test.py`, "python", TEST_REPO);
|
|
258
|
+
expect(result.baseUrl).toBe("http://localhost:8000");
|
|
259
|
+
});
|
|
260
|
+
});
|
|
@@ -13,18 +13,24 @@ export declare const serviceSchema: z.ZodObject<{
|
|
|
13
13
|
authHeader: z.ZodOptional<z.ZodString>;
|
|
14
14
|
authScheme: z.ZodOptional<z.ZodString>;
|
|
15
15
|
baseUrl: z.ZodOptional<z.ZodString>;
|
|
16
|
+
/** SUT serves self-signed TLS (e.g. a kind kube-apiserver) — generated
|
|
17
|
+
* tests should skip certificate verification for this service (SKYR-3961).
|
|
18
|
+
* Maps to the generation tools' `insecure` parameter / CLI `--insecure`. */
|
|
19
|
+
skipTLSVerify: z.ZodOptional<z.ZodBoolean>;
|
|
16
20
|
}, "strict", z.ZodTypeAny, {
|
|
17
21
|
authHeader?: string | undefined;
|
|
18
22
|
authScheme?: string | undefined;
|
|
19
23
|
schemaPath?: string | undefined;
|
|
20
24
|
authType?: "none" | "token" | "bearer" | "basic" | "oauth" | "apiKey" | "cookie" | "session" | undefined;
|
|
21
25
|
baseUrl?: string | undefined;
|
|
26
|
+
skipTLSVerify?: boolean | undefined;
|
|
22
27
|
}, {
|
|
23
28
|
authHeader?: string | undefined;
|
|
24
29
|
authScheme?: string | undefined;
|
|
25
30
|
schemaPath?: string | undefined;
|
|
26
31
|
authType?: "none" | "token" | "bearer" | "basic" | "oauth" | "apiKey" | "cookie" | "session" | undefined;
|
|
27
32
|
baseUrl?: string | undefined;
|
|
33
|
+
skipTLSVerify?: boolean | undefined;
|
|
28
34
|
}>>;
|
|
29
35
|
runtimeDetails: z.ZodOptional<z.ZodObject<{
|
|
30
36
|
serverStartCommand: z.ZodOptional<z.ZodString>;
|
|
@@ -57,6 +63,7 @@ export declare const serviceSchema: z.ZodObject<{
|
|
|
57
63
|
schemaPath?: string | undefined;
|
|
58
64
|
authType?: "none" | "token" | "bearer" | "basic" | "oauth" | "apiKey" | "cookie" | "session" | undefined;
|
|
59
65
|
baseUrl?: string | undefined;
|
|
66
|
+
skipTLSVerify?: boolean | undefined;
|
|
60
67
|
} | undefined;
|
|
61
68
|
runtimeDetails?: {
|
|
62
69
|
runtime: "local" | "docker" | "k8s";
|
|
@@ -77,6 +84,7 @@ export declare const serviceSchema: z.ZodObject<{
|
|
|
77
84
|
schemaPath?: string | undefined;
|
|
78
85
|
authType?: "none" | "token" | "bearer" | "basic" | "oauth" | "apiKey" | "cookie" | "session" | undefined;
|
|
79
86
|
baseUrl?: string | undefined;
|
|
87
|
+
skipTLSVerify?: boolean | undefined;
|
|
80
88
|
} | undefined;
|
|
81
89
|
runtimeDetails?: {
|
|
82
90
|
runtime: "local" | "docker" | "k8s";
|
|
@@ -128,18 +136,24 @@ export declare const workspaceConfigSchema: z.ZodObject<{
|
|
|
128
136
|
authHeader: z.ZodOptional<z.ZodString>;
|
|
129
137
|
authScheme: z.ZodOptional<z.ZodString>;
|
|
130
138
|
baseUrl: z.ZodOptional<z.ZodString>;
|
|
139
|
+
/** SUT serves self-signed TLS (e.g. a kind kube-apiserver) — generated
|
|
140
|
+
* tests should skip certificate verification for this service (SKYR-3961).
|
|
141
|
+
* Maps to the generation tools' `insecure` parameter / CLI `--insecure`. */
|
|
142
|
+
skipTLSVerify: z.ZodOptional<z.ZodBoolean>;
|
|
131
143
|
}, "strict", z.ZodTypeAny, {
|
|
132
144
|
authHeader?: string | undefined;
|
|
133
145
|
authScheme?: string | undefined;
|
|
134
146
|
schemaPath?: string | undefined;
|
|
135
147
|
authType?: "none" | "token" | "bearer" | "basic" | "oauth" | "apiKey" | "cookie" | "session" | undefined;
|
|
136
148
|
baseUrl?: string | undefined;
|
|
149
|
+
skipTLSVerify?: boolean | undefined;
|
|
137
150
|
}, {
|
|
138
151
|
authHeader?: string | undefined;
|
|
139
152
|
authScheme?: string | undefined;
|
|
140
153
|
schemaPath?: string | undefined;
|
|
141
154
|
authType?: "none" | "token" | "bearer" | "basic" | "oauth" | "apiKey" | "cookie" | "session" | undefined;
|
|
142
155
|
baseUrl?: string | undefined;
|
|
156
|
+
skipTLSVerify?: boolean | undefined;
|
|
143
157
|
}>>;
|
|
144
158
|
runtimeDetails: z.ZodOptional<z.ZodObject<{
|
|
145
159
|
serverStartCommand: z.ZodOptional<z.ZodString>;
|
|
@@ -172,6 +186,7 @@ export declare const workspaceConfigSchema: z.ZodObject<{
|
|
|
172
186
|
schemaPath?: string | undefined;
|
|
173
187
|
authType?: "none" | "token" | "bearer" | "basic" | "oauth" | "apiKey" | "cookie" | "session" | undefined;
|
|
174
188
|
baseUrl?: string | undefined;
|
|
189
|
+
skipTLSVerify?: boolean | undefined;
|
|
175
190
|
} | undefined;
|
|
176
191
|
runtimeDetails?: {
|
|
177
192
|
runtime: "local" | "docker" | "k8s";
|
|
@@ -192,6 +207,7 @@ export declare const workspaceConfigSchema: z.ZodObject<{
|
|
|
192
207
|
schemaPath?: string | undefined;
|
|
193
208
|
authType?: "none" | "token" | "bearer" | "basic" | "oauth" | "apiKey" | "cookie" | "session" | undefined;
|
|
194
209
|
baseUrl?: string | undefined;
|
|
210
|
+
skipTLSVerify?: boolean | undefined;
|
|
195
211
|
} | undefined;
|
|
196
212
|
runtimeDetails?: {
|
|
197
213
|
runtime: "local" | "docker" | "k8s";
|
|
@@ -225,6 +241,7 @@ export declare const workspaceConfigSchema: z.ZodObject<{
|
|
|
225
241
|
schemaPath?: string | undefined;
|
|
226
242
|
authType?: "none" | "token" | "bearer" | "basic" | "oauth" | "apiKey" | "cookie" | "session" | undefined;
|
|
227
243
|
baseUrl?: string | undefined;
|
|
244
|
+
skipTLSVerify?: boolean | undefined;
|
|
228
245
|
} | undefined;
|
|
229
246
|
runtimeDetails?: {
|
|
230
247
|
runtime: "local" | "docker" | "k8s";
|
|
@@ -258,6 +275,7 @@ export declare const workspaceConfigSchema: z.ZodObject<{
|
|
|
258
275
|
schemaPath?: string | undefined;
|
|
259
276
|
authType?: "none" | "token" | "bearer" | "basic" | "oauth" | "apiKey" | "cookie" | "session" | undefined;
|
|
260
277
|
baseUrl?: string | undefined;
|
|
278
|
+
skipTLSVerify?: boolean | undefined;
|
|
261
279
|
} | undefined;
|
|
262
280
|
runtimeDetails?: {
|
|
263
281
|
runtime: "local" | "docker" | "k8s";
|
|
@@ -299,6 +317,7 @@ export declare function validateWorkspaceConfig(config: unknown): z.SafeParseRet
|
|
|
299
317
|
schemaPath?: string | undefined;
|
|
300
318
|
authType?: "none" | "token" | "bearer" | "basic" | "oauth" | "apiKey" | "cookie" | "session" | undefined;
|
|
301
319
|
baseUrl?: string | undefined;
|
|
320
|
+
skipTLSVerify?: boolean | undefined;
|
|
302
321
|
} | undefined;
|
|
303
322
|
runtimeDetails?: {
|
|
304
323
|
runtime: "local" | "docker" | "k8s";
|
|
@@ -332,6 +351,7 @@ export declare function validateWorkspaceConfig(config: unknown): z.SafeParseRet
|
|
|
332
351
|
schemaPath?: string | undefined;
|
|
333
352
|
authType?: "none" | "token" | "bearer" | "basic" | "oauth" | "apiKey" | "cookie" | "session" | undefined;
|
|
334
353
|
baseUrl?: string | undefined;
|
|
354
|
+
skipTLSVerify?: boolean | undefined;
|
|
335
355
|
} | undefined;
|
|
336
356
|
runtimeDetails?: {
|
|
337
357
|
runtime: "local" | "docker" | "k8s";
|
|
@@ -51,6 +51,10 @@ export const serviceSchema = z
|
|
|
51
51
|
authHeader: z.string().optional(),
|
|
52
52
|
authScheme: z.string().optional(),
|
|
53
53
|
baseUrl: z.string().optional(),
|
|
54
|
+
/** SUT serves self-signed TLS (e.g. a kind kube-apiserver) — generated
|
|
55
|
+
* tests should skip certificate verification for this service (SKYR-3961).
|
|
56
|
+
* Maps to the generation tools' `insecure` parameter / CLI `--insecure`. */
|
|
57
|
+
skipTLSVerify: z.boolean().optional(),
|
|
54
58
|
})
|
|
55
59
|
.strict()
|
|
56
60
|
.optional(),
|
|
@@ -16,6 +16,16 @@ describe("workspace module (ported from @skyramp/skyramp, SKYR-3794)", () => {
|
|
|
16
16
|
it("rejects unknown fields (strict)", () => {
|
|
17
17
|
expect(serviceSchema.safeParse({ serviceName: "api", bogus: true }).success).toBe(false);
|
|
18
18
|
});
|
|
19
|
+
it("accepts api.skipTLSVerify for self-signed-TLS SUTs and rejects non-boolean (SKYR-3961)", () => {
|
|
20
|
+
expect(serviceSchema.safeParse({
|
|
21
|
+
serviceName: "api",
|
|
22
|
+
api: { baseUrl: "https://127.0.0.1:45643", skipTLSVerify: true },
|
|
23
|
+
}).success).toBe(true);
|
|
24
|
+
expect(serviceSchema.safeParse({
|
|
25
|
+
serviceName: "api",
|
|
26
|
+
api: { baseUrl: "https://127.0.0.1:45643", skipTLSVerify: "yes" },
|
|
27
|
+
}).success).toBe(false);
|
|
28
|
+
});
|
|
19
29
|
});
|
|
20
30
|
describe("validateWorkspaceConfig / createDefaultConfig", () => {
|
|
21
31
|
it("a default config validates", () => {
|
|
@@ -1311,9 +1311,10 @@ Could not parse locator: ${locatorExpr}` }], isError: true };
|
|
|
1311
1311
|
details = passed ? `Value assertion passed: "${params.element}" has value "${params.expected}".` : `Value assertion FAILED: "${params.element}" has value "${elementValue}", expected "${params.expected}".`;
|
|
1312
1312
|
}
|
|
1313
1313
|
if (passed) {
|
|
1314
|
+
const recordedExpected = params.type === "text" && params.substring !== false ? (0, import_utils.stableTextLead)(params.expected) : params.expected;
|
|
1314
1315
|
const selectorText = parsed.locator.kind === "text" ? parsed.locator.body : parsed.locator.next?.kind === "text" ? parsed.locator.next.body : parsed.locator.options?.name ?? null;
|
|
1315
|
-
if (selectorText && selectorText ===
|
|
1316
|
-
traceDebug(`Skipped tautological assertion: selector already matches "${
|
|
1316
|
+
if (selectorText && selectorText === recordedExpected) {
|
|
1317
|
+
traceDebug(`Skipped tautological assertion: selector already matches "${recordedExpected}"`);
|
|
1317
1318
|
} else {
|
|
1318
1319
|
const assertName = params.type === "value" ? "assertValue" : "assertText";
|
|
1319
1320
|
this._trackedActions.push({
|
|
@@ -1321,8 +1322,8 @@ Could not parse locator: ${locatorExpr}` }], isError: true };
|
|
|
1321
1322
|
// Pass selector/expected as structured args so assertActionToJsonl can
|
|
1322
1323
|
// decode unambiguously (the colon-encoded code below corrupts values
|
|
1323
1324
|
// that contain ':', e.g. URLs). The code string is a fallback only.
|
|
1324
|
-
args: { type: params.type, ref: params.ref, selector: parsed.selector, expected:
|
|
1325
|
-
code: `${assertName}:${parsed.selector}:${
|
|
1325
|
+
args: { type: params.type, ref: params.ref, selector: parsed.selector, expected: recordedExpected, ...params.type === "text" ? { substring: params.substring } : {} },
|
|
1326
|
+
code: `${assertName}:${parsed.selector}:${recordedExpected}${params.type === "text" ? ":" + params.substring : ""}`,
|
|
1326
1327
|
timestamp
|
|
1327
1328
|
});
|
|
1328
1329
|
traceDebug(`Assert: ${assertName} with selector ${parsed.selector}`);
|
|
@@ -1571,14 +1572,14 @@ Cell row ${params.row}, column ${params.column} has "${actual}", expected "${par
|
|
|
1571
1572
|
const roleMatch = refLine.match(/^\s*-\s*(\w+)\s+"([^"]*)"/);
|
|
1572
1573
|
if (roleMatch && TraceRecordingBackend.ASSERTABLE_ROLES.has(roleMatch[1])) {
|
|
1573
1574
|
const role = roleMatch[1];
|
|
1574
|
-
const name = roleMatch[2];
|
|
1575
|
+
const name = (0, import_utils.stableTextLead)(roleMatch[2]);
|
|
1575
1576
|
return {
|
|
1576
1577
|
selector: `internal:role=${role}[name="${name}"i]`,
|
|
1577
1578
|
locator: { kind: "role", body: role, options: { attrs: [], exact: false, name } }
|
|
1578
1579
|
};
|
|
1579
1580
|
}
|
|
1580
1581
|
if (roleMatch && roleMatch[2]) {
|
|
1581
|
-
const text = roleMatch[2];
|
|
1582
|
+
const text = (0, import_utils.stableTextLead)(roleMatch[2]);
|
|
1582
1583
|
return {
|
|
1583
1584
|
selector: `internal:text="${text}"i`,
|
|
1584
1585
|
locator: { kind: "text", body: text, options: { exact: false } }
|
|
@@ -1586,7 +1587,7 @@ Cell row ${params.row}, column ${params.column} has "${actual}", expected "${par
|
|
|
1586
1587
|
}
|
|
1587
1588
|
const colonTextMatch = refLine.match(/\[ref=\w+\](?:\s*\[[^\]]*\])*\s*:\s*(.+?)\s*$/);
|
|
1588
1589
|
if (colonTextMatch && colonTextMatch[1].trim()) {
|
|
1589
|
-
const text = colonTextMatch[1].trim();
|
|
1590
|
+
const text = (0, import_utils.stableTextLead)(colonTextMatch[1].trim());
|
|
1590
1591
|
return {
|
|
1591
1592
|
selector: `internal:text="${text}"i`,
|
|
1592
1593
|
locator: { kind: "text", body: text, options: { exact: false } }
|
|
@@ -1686,6 +1687,71 @@ Cell row ${params.row}, column ${params.column} has "${actual}", expected "${par
|
|
|
1686
1687
|
target += `.locator('${frame.replace(/'/g, "\\'")}').contentFrame()`;
|
|
1687
1688
|
return `await ${target}.${expr}.${verb}(${callArgs});`;
|
|
1688
1689
|
}
|
|
1690
|
+
/**
|
|
1691
|
+
* SKYR-3840: re-anchor a fragile / index-suffixed testid LEAF onto a stable
|
|
1692
|
+
* anchor. Upstream generateSelector prefers a data-testid because it is unique
|
|
1693
|
+
* at record time, but an index-suffixed testid (choice-card-6, package-name-1)
|
|
1694
|
+
* is a positional counter that shifts when the list reorders or grows. When the
|
|
1695
|
+
* clicked element carries a stable name — its own, or an inner heading title for
|
|
1696
|
+
* card/tile widgets — anchor on role+name / text instead. Mirrors the manual
|
|
1697
|
+
* recorder's ScopingHandler fix (_isFragileTestId + h1-h6→heading) for the
|
|
1698
|
+
* tool-recording path, which codegen passes through verbatim.
|
|
1699
|
+
*
|
|
1700
|
+
* Only no-argument click-family verbs qualify: re-anchoring onto a role+name /
|
|
1701
|
+
* text locator is meaningful for clicking a card/label, but not for
|
|
1702
|
+
* fill/type/selectOption (their target is an input, never a heading/text node,
|
|
1703
|
+
* and the rewrite must not drop the call's value argument).
|
|
1704
|
+
*
|
|
1705
|
+
* Returns the rewritten result, or null to leave the selector unchanged.
|
|
1706
|
+
*/
|
|
1707
|
+
async _reanchorFragileTestidLeaf(code, args, result) {
|
|
1708
|
+
const testidLeafMatch = code.match(/^await\s+page\.getByTestId\(\s*(['"])([^'"]+)\1\s*\)\.(click|dblclick|check|uncheck|hover)\(/);
|
|
1709
|
+
if (!testidLeafMatch || !(0, import_dynamicId.isDynamicId)(testidLeafMatch[2]) || !args.ref)
|
|
1710
|
+
return null;
|
|
1711
|
+
const fragileTestid = testidLeafMatch[2];
|
|
1712
|
+
const action = testidLeafMatch[3];
|
|
1713
|
+
const ref = args.ref;
|
|
1714
|
+
const page = this._browserBackend.context?.currentTab()?.page;
|
|
1715
|
+
if (!page)
|
|
1716
|
+
return null;
|
|
1717
|
+
let anchor = null;
|
|
1718
|
+
try {
|
|
1719
|
+
anchor = await page.locator(`aria-ref=${ref}`).evaluate((el) => {
|
|
1720
|
+
const stableLabel = (t) => {
|
|
1721
|
+
if (!t) return null;
|
|
1722
|
+
const s = t.trim().replace(/\s+/g, " ");
|
|
1723
|
+
if (s.length < 2 || s.length > 50) return null;
|
|
1724
|
+
if (/[$£€]\s?\d|\d{4,}|\b\d{1,2}\/\d{1,2}\b/.test(s)) return null;
|
|
1725
|
+
return s;
|
|
1726
|
+
};
|
|
1727
|
+
const heading = el.querySelector('h1,h2,h3,h4,h5,h6,[role="heading"]');
|
|
1728
|
+
const hname = heading ? stableLabel(heading.textContent) : null;
|
|
1729
|
+
if (hname) return { kind: "heading", name: hname };
|
|
1730
|
+
const own = stableLabel([...el.childNodes].filter((n) => n.nodeType === Node.TEXT_NODE).map((n) => n.textContent).join(""));
|
|
1731
|
+
if (own) return { kind: "text", name: own };
|
|
1732
|
+
return null;
|
|
1733
|
+
});
|
|
1734
|
+
} catch (e) {
|
|
1735
|
+
traceDebug(`[reanchor] aria-ref evaluate failed for ref ${ref}: ${e}`);
|
|
1736
|
+
return null;
|
|
1737
|
+
}
|
|
1738
|
+
if (!anchor)
|
|
1739
|
+
return null;
|
|
1740
|
+
let count = 0;
|
|
1741
|
+
try {
|
|
1742
|
+
count = anchor.kind === "heading" ? await page.getByRole("heading", { name: anchor.name }).count() : await page.getByText(anchor.name).count();
|
|
1743
|
+
} catch (e) {
|
|
1744
|
+
traceDebug(`[reanchor] uniqueness count failed for "${anchor.name}": ${e}`);
|
|
1745
|
+
return null;
|
|
1746
|
+
}
|
|
1747
|
+
if (count !== 1) {
|
|
1748
|
+
traceDebug(`[reanchor] anchor "${anchor.name}" not unique (${count}); keeping testid`);
|
|
1749
|
+
return null;
|
|
1750
|
+
}
|
|
1751
|
+
const expr = anchor.kind === "heading" ? `getByRole('heading', { name: ${JSON.stringify(anchor.name)} })` : `getByText(${JSON.stringify(anchor.name)})`;
|
|
1752
|
+
traceDebug(`[reanchor] fragile testid "${fragileTestid}" -> ${expr}`);
|
|
1753
|
+
return this._modifyResultCode(result, `await page.${expr}.${action}();`);
|
|
1754
|
+
}
|
|
1689
1755
|
async _improveActionSelector(toolName, args, result, preClickHoverCode = null, preClickSnapshot = null, preClickLocatorCount = 0, preClickParentChain = null) {
|
|
1690
1756
|
if (result.isError)
|
|
1691
1757
|
return result;
|
|
@@ -1696,6 +1762,9 @@ Cell row ${params.row}, column ${params.column} has "${actual}", expected "${par
|
|
|
1696
1762
|
const code = parsed?.code ?? "";
|
|
1697
1763
|
if (!code)
|
|
1698
1764
|
return result;
|
|
1765
|
+
const reanchored = await this._reanchorFragileTestidLeaf(code, args, result);
|
|
1766
|
+
if (reanchored)
|
|
1767
|
+
return reanchored;
|
|
1699
1768
|
const cssLocatorMatch = code.match(/await\s+page\.locator\(\s*(['"])([.#][^'"]+)\1\s*\)\./);
|
|
1700
1769
|
if (cssLocatorMatch) {
|
|
1701
1770
|
const cssSelector = cssLocatorMatch[2];
|
|
@@ -1769,7 +1838,7 @@ Cell row ${params.row}, column ${params.column} has "${actual}", expected "${par
|
|
|
1769
1838
|
if (!roleName && ambiguousCount > 1) {
|
|
1770
1839
|
const textMatch = refLine.match(/^\s*-\s*\w+\s+"([^"]*)"/);
|
|
1771
1840
|
if (textMatch && textMatch[1]) {
|
|
1772
|
-
const visibleText = textMatch[1];
|
|
1841
|
+
const visibleText = (0, import_utils.stableTextLead)(textMatch[1]);
|
|
1773
1842
|
const textPattern = new RegExp(`^\\s*-\\s*${role}\\s+"[^"]*${visibleText.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}`, "i");
|
|
1774
1843
|
const textCount = snapLines.filter((l) => textPattern.test(l)).length;
|
|
1775
1844
|
if (textCount === 1) {
|