@skyramp/mcp 0.2.6 → 0.2.150-rc.mntnc
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/commands/commandLibrary.d.ts +12 -0
- package/build/commands/recommendTestsAndExecuteCommand.d.ts +2 -0
- package/build/commands/testThisEndpointCommand.d.ts +2 -0
- package/build/index.d.ts +2 -0
- package/build/index.js +6 -4
- package/build/playwright/PlaywrightTraceService.d.ts +32 -0
- package/build/playwright/index.d.ts +3 -0
- package/build/playwright/registerPlaywrightTools.d.ts +8 -0
- package/build/playwright/registerPlaywrightTools.js +12 -11
- package/build/playwright/traceRecordingPrompt.d.ts +13 -0
- package/build/playwright/traceRecordingPrompt.js +3 -11
- package/build/prompts/code-reuse.d.ts +1 -0
- package/build/prompts/enhance-assertions/contractProviderAssertionsPrompt.d.ts +2 -0
- package/build/prompts/enhance-assertions/integrationAssertionsPrompt.d.ts +2 -0
- package/build/prompts/enhance-assertions/sharedAssertionRules.d.ts +15 -0
- package/build/prompts/enhance-assertions/uiAssertionsPrompt.d.ts +2 -0
- package/build/prompts/enhance-assertions/uiAssertionsPrompt.js +24 -5
- package/build/prompts/fix-error-prompt.d.ts +1 -0
- package/build/prompts/initialize-workspace/initializeWorkspacePrompt.d.ts +1 -0
- package/build/prompts/modularization/integration-test-modularization.d.ts +1 -0
- package/build/prompts/modularization/ui-test-modularization.d.ts +1 -0
- package/build/prompts/personas.d.ts +21 -0
- package/build/prompts/pom-aware-code-reuse.d.ts +1 -0
- package/build/prompts/startTraceCollectionPrompts.d.ts +2 -0
- package/build/prompts/sut-setup/modes/adaptWorkflowPrompt.d.ts +2 -0
- package/build/prompts/sut-setup/modes/adaptWorkflowPrompt.js +114 -0
- package/build/prompts/sut-setup/modes/dockerComposePrompt.d.ts +2 -0
- package/build/prompts/sut-setup/modes/dockerComposePrompt.js +292 -0
- package/build/prompts/sut-setup/shared.d.ts +39 -0
- package/build/prompts/sut-setup/shared.js +132 -0
- package/build/prompts/test-maintenance/actionsInstructions.d.ts +35 -0
- package/build/prompts/test-maintenance/drift-analysis-prompt.d.ts +4 -0
- package/build/prompts/test-maintenance/driftAnalysisSections.d.ts +13 -0
- package/build/prompts/test-maintenance/driftAnalysisSections.js +9 -3
- package/build/prompts/test-recommendation/analysisOutputPrompt.d.ts +84 -0
- package/build/prompts/test-recommendation/analysisOutputPrompt.js +1 -1
- package/build/prompts/test-recommendation/analysisOutputPrompt.test.d.ts +1 -0
- package/build/prompts/test-recommendation/analysisOutputPrompt.test.js +1 -0
- package/build/prompts/test-recommendation/diffExecutionPlan.d.ts +17 -0
- package/build/prompts/test-recommendation/diffExecutionPlan.js +7 -16
- package/build/prompts/test-recommendation/fullRepoCatalog.d.ts +7 -0
- package/build/prompts/test-recommendation/mergeEnrichedScenarios.test.d.ts +1 -0
- package/build/prompts/test-recommendation/mergeEnrichedScenarios.test.js +1 -0
- package/build/prompts/test-recommendation/promptPlan.d.ts +203 -0
- package/build/prompts/test-recommendation/promptPlan.test.d.ts +1 -0
- package/build/prompts/test-recommendation/recommendationSections.d.ts +35 -0
- package/build/prompts/test-recommendation/recommendationShared.d.ts +31 -0
- package/build/prompts/test-recommendation/registerRecommendTestsPrompt.d.ts +7 -0
- package/build/prompts/test-recommendation/scopeAssessment.d.ts +55 -0
- package/build/prompts/test-recommendation/scopeAssessment.js +2 -2
- package/build/prompts/test-recommendation/scopeAssessment.test.d.ts +1 -0
- package/build/prompts/test-recommendation/scopeAssessment.test.js +1 -0
- package/build/prompts/test-recommendation/test-recommendation-prompt.d.ts +6 -0
- package/build/prompts/test-recommendation/test-recommendation-prompt.js +4 -4
- package/build/prompts/test-recommendation/test-recommendation-prompt.test.d.ts +1 -0
- package/build/prompts/test-recommendation/test-recommendation-prompt.test.js +6 -99
- package/build/prompts/testbot/testbot-prompts.d.ts +11 -0
- package/build/prompts/testbot/testbot-prompts.js +213 -42
- package/build/prompts/testbot/testbot-prompts.test.d.ts +1 -0
- package/build/prompts/testbot/testbot-prompts.test.js +15 -117
- package/build/resources/analysisResources.d.ts +17 -0
- package/build/resources/progressResource.d.ts +2 -0
- package/build/resources/sutSetupResource.d.ts +2 -0
- package/build/resources/sutSetupResource.js +45 -0
- package/build/resources/testbotResource.d.ts +2 -0
- package/build/resources/testbotResource.js +1 -1
- package/build/services/AnalyticsService.d.ts +13 -0
- package/build/services/AnalyticsService.test.d.ts +1 -0
- package/build/services/AnalyticsService.test.js +86 -0
- package/build/services/ModularizationService.d.ts +11 -0
- package/build/services/ScenarioGenerationService.d.ts +37 -0
- package/build/services/ScenarioGenerationService.integration.test.d.ts +1 -0
- package/build/services/ScenarioGenerationService.integration.test.js +4 -0
- package/build/services/ScenarioGenerationService.test.d.ts +1 -0
- package/build/services/TestDiscoveryService.d.ts +128 -0
- package/build/services/TestDiscoveryService.js +22 -12
- package/build/services/TestDiscoveryService.test.d.ts +1 -0
- package/build/services/TestDiscoveryService.test.js +4 -3
- package/build/services/TestExecutionService.d.ts +55 -0
- package/build/services/TestExecutionService.js +158 -26
- package/build/services/TestExecutionService.test.d.ts +1 -0
- package/build/services/TestExecutionService.test.js +339 -25
- package/build/services/TestGenerationService.d.ts +56 -0
- package/build/services/TestGenerationService.test.d.ts +1 -0
- package/build/services/TestGenerationService.test.js +10 -8
- package/build/services/containerEnv.d.ts +14 -0
- package/build/tool-phase-coverage.test.d.ts +1 -0
- package/build/tool-phase-coverage.test.js +2 -0
- package/build/tool-phases.d.ts +38 -0
- package/build/tools/auth/loginTool.d.ts +2 -0
- package/build/tools/auth/logoutTool.d.ts +2 -0
- package/build/tools/code-refactor/codeReuseTool.d.ts +2 -0
- package/build/tools/code-refactor/enhanceAssertionsTool.d.ts +2 -0
- package/build/tools/code-refactor/modularizationTool.d.ts +2 -0
- package/build/tools/executeSkyrampTestTool.d.ts +2 -0
- package/build/tools/executeSkyrampTestTool.js +17 -4
- package/build/tools/fixErrorTool.d.ts +2 -0
- package/build/tools/generate-tests/generateBatchScenarioRestTool.d.ts +45 -0
- package/build/tools/generate-tests/generateBatchScenarioRestTool.js +1 -1
- package/build/tools/generate-tests/generateBatchScenarioRestTool.test.d.ts +1 -0
- package/build/tools/generate-tests/generateBatchScenarioRestTool.test.js +11 -9
- package/build/tools/generate-tests/generateContractRestTool.d.ts +31 -0
- package/build/tools/generate-tests/generateE2ERestTool.d.ts +13 -0
- package/build/tools/generate-tests/generateFuzzRestTool.d.ts +10 -0
- package/build/tools/generate-tests/generateIntegrationRestTool.d.ts +16 -0
- package/build/tools/generate-tests/generateLoadRestTool.d.ts +18 -0
- package/build/tools/generate-tests/generateLoadRestTool.test.d.ts +1 -0
- package/build/tools/generate-tests/generateMockRestTool.d.ts +64 -0
- package/build/tools/generate-tests/generateSmokeRestTool.d.ts +8 -0
- package/build/tools/generate-tests/generateUIRestTool.d.ts +14 -0
- package/build/tools/generate-tests/generateUIRestTool.js +1 -1
- package/build/tools/generate-tests/loadTestSchema.d.ts +39 -0
- package/build/tools/one-click/oneClickTool.d.ts +11 -0
- package/build/tools/submitReportTool.d.ts +384 -0
- package/build/tools/submitReportTool.js +225 -37
- package/build/tools/submitReportTool.test.d.ts +1 -0
- package/build/tools/submitReportTool.test.js +479 -55
- package/build/tools/test-management/actionsTool.d.ts +10 -0
- package/build/tools/test-management/actionsTool.js +10 -0
- package/build/tools/test-management/analyzeChangesTool.d.ts +26 -0
- package/build/tools/test-management/analyzeChangesTool.js +128 -48
- package/build/tools/test-management/analyzeChangesTool.test.d.ts +1 -0
- package/build/tools/test-management/analyzeChangesTool.test.js +80 -73
- package/build/tools/test-management/analyzeTestHealthTool.d.ts +2 -0
- package/build/tools/test-management/analyzeTestHealthTool.js +6 -3
- package/build/tools/test-management/index.d.ts +3 -0
- package/build/tools/test-management/index.js +0 -1
- package/build/tools/trace/resolveSaveStoragePath.d.ts +7 -0
- package/build/tools/trace/resolveSaveStoragePath.test.d.ts +1 -0
- package/build/tools/trace/resolveSessionPaths.d.ts +40 -0
- package/build/tools/trace/resolveSessionPaths.test.d.ts +1 -0
- package/build/tools/trace/sessionState.d.ts +2 -0
- package/build/tools/trace/sessionState.test.d.ts +1 -0
- package/build/tools/trace/startTraceCollectionTool.d.ts +2 -0
- package/build/tools/trace/stopTraceCollectionTool.d.ts +2 -0
- package/build/tools/workspace/initScanWorkspaceTool.d.ts +4 -0
- package/build/tools/workspace/initializeWorkspaceTool.d.ts +2 -0
- package/build/tools/workspace/initializeWorkspaceTool.test.d.ts +1 -0
- package/build/tools/workspace/initializeWorkspaceTool.test.js +134 -0
- package/build/types/OneClickCommands.d.ts +68 -0
- package/build/types/RepositoryAnalysis.d.ts +3004 -0
- package/build/types/TestAnalysis.d.ts +97 -0
- package/build/types/TestExecution.d.ts +63 -0
- package/build/types/TestExecution.js +11 -1
- package/build/types/TestRecommendation.d.ts +12 -0
- package/build/types/TestTypes.d.ts +234 -0
- package/build/types/TestbotReport.d.ts +68 -0
- package/build/types/TestbotReport.js +1 -0
- package/build/types/index.d.ts +4 -0
- package/build/types/index.js +3 -0
- package/build/utils/AnalysisStateManager.d.ts +182 -0
- package/build/utils/AnalysisStateManager.js +10 -1
- package/build/utils/AnalysisStateManager.test.d.ts +1 -0
- package/build/utils/analyze-openapi.d.ts +1 -0
- package/build/utils/branchDiff.d.ts +18 -0
- package/build/utils/dartRouteExtractor.d.ts +45 -0
- package/build/utils/dartRouteExtractor.test.d.ts +1 -0
- package/build/utils/docker.d.ts +19 -0
- package/build/utils/docker.test.d.ts +1 -0
- package/build/utils/docker.test.js +10 -9
- package/build/utils/featureFlags.d.ts +37 -0
- package/build/utils/featureFlags.test.d.ts +1 -0
- package/build/utils/gitStaging.d.ts +24 -0
- package/build/utils/gitStaging.test.d.ts +1 -0
- package/build/utils/gitStaging.test.js +13 -7
- package/build/utils/httpDefaults.d.ts +10 -0
- package/build/utils/httpDefaults.test.d.ts +1 -0
- package/build/utils/initAgent.d.ts +36 -0
- package/build/utils/language-helper.d.ts +6 -0
- package/build/utils/logger.d.ts +11 -0
- package/build/utils/normalizeParams.d.ts +14 -0
- package/build/utils/normalizeSkyrampImports.d.ts +13 -0
- package/build/utils/pr-comment-parser.d.ts +31 -0
- package/build/utils/pr-comment-parser.test.d.ts +1 -0
- package/build/utils/pr-comment-parser.test.js +6 -5
- package/build/utils/projectMetadata.d.ts +15 -0
- package/build/utils/projectMetadata.test.d.ts +1 -0
- package/build/utils/proxy-terminal.d.ts +2 -0
- package/build/utils/repoScanner.d.ts +27 -0
- package/build/utils/repoScanner.test.d.ts +1 -0
- package/build/utils/routeParsers.d.ts +53 -0
- package/build/utils/routeParsers.js +31 -38
- package/build/utils/routeParsers.test.d.ts +1 -0
- package/build/utils/scenarioDrafting.d.ts +71 -0
- package/build/utils/scenarioDrafting.js +23 -1
- package/build/utils/scenarioDrafting.test.d.ts +1 -0
- package/build/utils/skyrampMdContent.d.ts +5 -0
- package/build/utils/sourceRouteExtractor.d.ts +48 -0
- package/build/utils/sourceRouteExtractor.test.d.ts +1 -0
- package/build/utils/telemetry.d.ts +32 -0
- package/build/utils/telemetry.test.d.ts +1 -0
- package/build/utils/trace-parser.d.ts +32 -0
- package/build/utils/trace-parser.test.d.ts +6 -0
- package/build/utils/uiPageEnumerator.d.ts +146 -0
- package/build/utils/uiPageEnumerator.js +58 -14
- package/build/utils/uiPageEnumerator.test.d.ts +1 -0
- package/build/utils/uiPageEnumerator.test.js +223 -43
- package/build/utils/utils.d.ts +45 -0
- package/build/utils/utils.js +10 -0
- package/build/utils/utils.test.d.ts +1 -0
- package/build/utils/versions.d.ts +3 -0
- package/build/utils/versions.js +1 -1
- package/build/utils/workspaceAuth.d.ts +127 -0
- package/build/utils/workspaceAuth.test.d.ts +1 -0
- package/build/utils/workspaceAuth.test.js +25 -25
- package/build/workspace/index.d.ts +1 -0
- package/build/workspace/workspace.d.ts +390 -0
- package/build/workspace/workspace.test.d.ts +1 -0
- package/node_modules/playwright/LICENSE +202 -0
- package/node_modules/playwright/NOTICE +5 -0
- package/node_modules/playwright/README.md +168 -0
- package/node_modules/playwright/lib/mcp/skyramp/traceRecordingBackend.js +13 -9
- package/node_modules/playwright/lib/mcp/test/skyRampExport.js +4 -3
- package/node_modules/playwright/node_modules/playwright-core/LICENSE +202 -0
- package/node_modules/playwright/node_modules/playwright-core/NOTICE +5 -0
- package/node_modules/playwright/node_modules/playwright-core/ThirdPartyNotices.txt +23 -126
- package/node_modules/playwright/node_modules/playwright-core/lib/vite/traceViewer/assets/{codeMirrorModule-B0JOjboO.js → codeMirrorModule-B5kqh2EV.js} +1 -1
- package/node_modules/playwright/node_modules/playwright-core/lib/vite/traceViewer/assets/{defaultSettingsView-1anWeyDf.js → defaultSettingsView-CZ9npQ3N.js} +79 -79
- package/node_modules/playwright/node_modules/playwright-core/lib/vite/traceViewer/{index.Cc5029a3.js → index.BCnxj-_b.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.Wo5yvvVh.js → uiMode.1Ym0Ivn8.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/package.json +1 -1
- package/package.json +16 -4
- package/build/tools/test-management/uiAnalyzeChangesTool.js +0 -155
- package/build/tools/test-management/uiAnalyzeChangesTool.test.js +0 -147
- package/node_modules/playwright/node_modules/playwright-core/.DS_Store +0 -0
- package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/.bin/node-which +0 -52
- package/node_modules/playwright/node_modules/playwright-core/bundles/utils/node_modules/.bin/is-docker +0 -5
- package/node_modules/playwright/node_modules/playwright-core/bundles/utils/node_modules/.bin/mime +0 -46
- package/node_modules/playwright/node_modules/playwright-core/bundles/utils/node_modules/.bin/yaml +0 -11
- package/node_modules/playwright/node_modules/playwright-core/lib/vite/traceViewer/index.CP3Y8Zsb.js +0 -2
|
@@ -102,9 +102,7 @@ export class TestDiscoveryService {
|
|
|
102
102
|
this.partitionByRelevance(classified.external, changedResources));
|
|
103
103
|
}
|
|
104
104
|
else if (changedResources !== undefined) {
|
|
105
|
-
// PR mode with
|
|
106
|
-
// context with irrelevant external tests. The LLM will work from Skyramp tests
|
|
107
|
-
// and scanned endpoints only.
|
|
105
|
+
// PR mode with no path-resolvable endpoints — no path-based scoring available.
|
|
108
106
|
relevantExternal = [];
|
|
109
107
|
otherExternal = [];
|
|
110
108
|
}
|
|
@@ -151,27 +149,39 @@ export class TestDiscoveryService {
|
|
|
151
149
|
// Use the last two path segments to avoid false matches from deeply-nested dirs
|
|
152
150
|
const segments = normalized.split("/").slice(-2).join("/");
|
|
153
151
|
const tokens = new Set(segments.split(/[/\-_.]+/).filter(Boolean));
|
|
152
|
+
// Expand tokens with plural/singular variants so "deployment" matches
|
|
153
|
+
// "deployments" and vice versa. Only strip trailing "s" when the result
|
|
154
|
+
// is ≥4 chars (avoids "pas"→"pa") and doesn't end in "ss" (avoids "pass"→"pas").
|
|
155
|
+
const expanded = new Set(tokens);
|
|
156
|
+
for (const t of tokens) {
|
|
157
|
+
if (t.endsWith("s") && !t.endsWith("ss") && t.length > 4)
|
|
158
|
+
expanded.add(t.slice(0, -1));
|
|
159
|
+
else if (!t.endsWith("s"))
|
|
160
|
+
expanded.add(t + "s");
|
|
161
|
+
}
|
|
154
162
|
return changedResources.filter(r => {
|
|
155
163
|
const rLower = r.toLowerCase();
|
|
156
|
-
|
|
157
|
-
if (tokens.has(rLower))
|
|
164
|
+
if (expanded.has(rLower))
|
|
158
165
|
return true;
|
|
159
|
-
// Compound resource names (e.g. "order-items"
|
|
160
|
-
// that ALL hyphen/underscore-separated parts appear as individual tokens.
|
|
161
|
-
// This handles test_order_items.py correctly matching resource "order-items".
|
|
166
|
+
// Compound resource names (e.g. "order-items") — all parts must appear as tokens.
|
|
162
167
|
const parts = rLower.split(/[-_]/);
|
|
163
|
-
return parts.length > 1 && parts.every(p => p.length >= 3 &&
|
|
168
|
+
return parts.length > 1 && parts.every(p => p.length >= 3 && expanded.has(p));
|
|
164
169
|
}).length;
|
|
165
170
|
}
|
|
166
171
|
/**
|
|
167
|
-
* Partition external test files into relevant (score > 0) and low-relevance (score = 0)
|
|
168
|
-
*
|
|
172
|
+
* Partition external test files into relevant (score > 0) and low-relevance (score = 0).
|
|
173
|
+
* Signal: path/name token overlap with changedResources.
|
|
169
174
|
*/
|
|
170
175
|
partitionByRelevance(files, changedResources) {
|
|
171
176
|
const relevant = [];
|
|
172
177
|
const other = [];
|
|
173
178
|
for (const f of files) {
|
|
174
|
-
(this.scoreRelevance(f, changedResources) > 0
|
|
179
|
+
if (this.scoreRelevance(f, changedResources) > 0) {
|
|
180
|
+
relevant.push(f);
|
|
181
|
+
}
|
|
182
|
+
else {
|
|
183
|
+
other.push(f);
|
|
184
|
+
}
|
|
175
185
|
}
|
|
176
186
|
return { relevant, other };
|
|
177
187
|
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -1,10 +1,12 @@
|
|
|
1
|
+
// @ts-nocheck - Jest ESM type inference issues
|
|
2
|
+
import { jest } from "@jest/globals";
|
|
1
3
|
// Mock heavy dependencies
|
|
2
4
|
jest.mock("simple-git", () => ({
|
|
3
5
|
simpleGit: jest.fn(() => ({
|
|
4
6
|
checkIsRepo: jest.fn().mockResolvedValue(false),
|
|
5
7
|
})),
|
|
6
8
|
}));
|
|
7
|
-
jest.
|
|
9
|
+
jest.unstable_mockModule("../utils/logger.js", () => ({
|
|
8
10
|
logger: {
|
|
9
11
|
info: jest.fn(),
|
|
10
12
|
debug: jest.fn(),
|
|
@@ -14,12 +16,11 @@ jest.mock("../utils/logger.js", () => ({
|
|
|
14
16
|
}));
|
|
15
17
|
// fast-glob must return real file paths from the temp directory.
|
|
16
18
|
// We use the real implementation by not mocking it.
|
|
17
|
-
// Jest resolves .js → .ts via moduleNameMapper in jest.config.
|
|
18
19
|
import * as fs from "fs";
|
|
19
20
|
import * as path from "path";
|
|
20
21
|
import * as os from "os";
|
|
21
|
-
import { TestDiscoveryService } from "./TestDiscoveryService.js";
|
|
22
22
|
import { TestSource } from "../types/TestAnalysis.js";
|
|
23
|
+
const { TestDiscoveryService } = await import("./TestDiscoveryService.js");
|
|
23
24
|
describe("TestDiscoveryService", () => {
|
|
24
25
|
let service;
|
|
25
26
|
let tmpDir;
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { TestExecutionResult, BatchExecutionResult, TestExecutionOptions, ProgressCallback } from "../types/TestExecution.js";
|
|
2
|
+
export declare const EXECUTOR_DOCKER_IMAGE = "skyramp/executor:v1.3.28";
|
|
3
|
+
export declare const PLAYWRIGHT_CONFIG_FILES: string[];
|
|
4
|
+
export declare const EXCLUDED_MOUNT_ITEMS: string[];
|
|
5
|
+
export declare const MOUNT_NULL_ITEMS: string[];
|
|
6
|
+
/**
|
|
7
|
+
* Check if a test type requires browser execution (UI/E2E)
|
|
8
|
+
*/
|
|
9
|
+
export declare function isBrowserTest(testType: string): boolean;
|
|
10
|
+
/**
|
|
11
|
+
* Generate a deterministic unique subdirectory name for video output
|
|
12
|
+
* based on the test file path
|
|
13
|
+
*/
|
|
14
|
+
export declare function getVideoSubdir(testFile: string): string;
|
|
15
|
+
/**
|
|
16
|
+
* Detect session file paths referenced in test files
|
|
17
|
+
* Looks for storageState patterns in TypeScript/JavaScript/Python/Java/C# test files
|
|
18
|
+
* Excludes matches found in comments
|
|
19
|
+
*
|
|
20
|
+
* Also handles the codegen pattern `path.join(__dirname, '<filename>')` (TS/JS) —
|
|
21
|
+
* the filename is resolved relative to the test file's directory on the host so
|
|
22
|
+
* the existing absolute-path mount branch makes it visible at the same path
|
|
23
|
+
* inside the container (Playwright's TS loader resolves __dirname to the host
|
|
24
|
+
* workspace path at runtime).
|
|
25
|
+
*/
|
|
26
|
+
export declare function detectSessionFiles(testFilePath: string): string[];
|
|
27
|
+
export declare class TestExecutionService {
|
|
28
|
+
private docker;
|
|
29
|
+
private imageReady;
|
|
30
|
+
constructor();
|
|
31
|
+
/**
|
|
32
|
+
* Execute multiple tests in parallel batches
|
|
33
|
+
*/
|
|
34
|
+
executeBatch(testOptions: TestExecutionOptions[]): Promise<BatchExecutionResult>;
|
|
35
|
+
/**
|
|
36
|
+
* Execute a single test
|
|
37
|
+
* @param options Test execution options
|
|
38
|
+
* @param onProgress Optional callback for progress updates
|
|
39
|
+
*/
|
|
40
|
+
executeTest(options: TestExecutionOptions, onProgress?: ProgressCallback): Promise<TestExecutionResult>;
|
|
41
|
+
/**
|
|
42
|
+
* Ensure Docker image is available
|
|
43
|
+
* @param onProgress Optional callback for progress updates during pull
|
|
44
|
+
* @returns Object indicating whether image was cached or pulled
|
|
45
|
+
*/
|
|
46
|
+
private ensureDockerImage;
|
|
47
|
+
/**
|
|
48
|
+
* Parse errors from test output
|
|
49
|
+
*/
|
|
50
|
+
private parseErrors;
|
|
51
|
+
/**
|
|
52
|
+
* Parse warnings from test output
|
|
53
|
+
*/
|
|
54
|
+
private parseWarnings;
|
|
55
|
+
}
|
|
@@ -2,9 +2,12 @@ import Docker from "dockerode";
|
|
|
2
2
|
import path from "path";
|
|
3
3
|
import fs from "fs";
|
|
4
4
|
import os from "os";
|
|
5
|
+
import crypto from "crypto";
|
|
5
6
|
import { Writable } from "stream";
|
|
6
7
|
import { stripVTControlCharacters } from "util";
|
|
7
8
|
import { logger } from "../utils/logger.js";
|
|
9
|
+
import { TestExecutionStatus, } from "../types/TestExecution.js";
|
|
10
|
+
import { TestType } from "../types/TestTypes.js";
|
|
8
11
|
import { buildContainerEnv } from "./containerEnv.js";
|
|
9
12
|
import { SKYRAMP_IMAGE_VERSION } from "../utils/versions.js";
|
|
10
13
|
const DEFAULT_TIMEOUT = 300000; // 5 minutes
|
|
@@ -47,6 +50,109 @@ export const MOUNT_NULL_ITEMS = [
|
|
|
47
50
|
"__init__.py",
|
|
48
51
|
"conftest.py",
|
|
49
52
|
];
|
|
53
|
+
/**
|
|
54
|
+
* Check if a test type requires browser execution (UI/E2E)
|
|
55
|
+
*/
|
|
56
|
+
export function isBrowserTest(testType) {
|
|
57
|
+
return testType === TestType.UI || testType === TestType.E2E;
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Generate a deterministic unique subdirectory name for video output
|
|
61
|
+
* based on the test file path
|
|
62
|
+
*/
|
|
63
|
+
export function getVideoSubdir(testFile) {
|
|
64
|
+
const basename = path.basename(testFile, path.extname(testFile)).replace(/[^A-Za-z0-9._-]/g, "-");
|
|
65
|
+
const hash = crypto.createHash("sha256").update(testFile).digest("hex").slice(0, 8);
|
|
66
|
+
return `${basename}-${hash}`;
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* For UI/E2E tests, create a per-execution video output directory under the
|
|
70
|
+
* workspace and generate the language-specific runner config that enables
|
|
71
|
+
* recording (Playwright config for TS/JS, pytest.ini for Python). The returned
|
|
72
|
+
* paths are mounted into the executor container by the caller.
|
|
73
|
+
*
|
|
74
|
+
* No-op (returns disabled defaults) for non-browser test types.
|
|
75
|
+
*/
|
|
76
|
+
function setupVideoCapture(options, workspacePath, containerMountPath) {
|
|
77
|
+
const browserTest = isBrowserTest(options.testType);
|
|
78
|
+
const setup = {
|
|
79
|
+
browserTest,
|
|
80
|
+
videoHostDir: "",
|
|
81
|
+
videoContainerDir: "",
|
|
82
|
+
playwrightConfigPath: MINIMAL_PLAYWRIGHT_CONFIG_PATH,
|
|
83
|
+
pytestConfigPath: "",
|
|
84
|
+
};
|
|
85
|
+
if (!browserTest)
|
|
86
|
+
return setup;
|
|
87
|
+
// Per-execution video output directory on host (cleared so a retry never
|
|
88
|
+
// reports a stale recording from a prior run).
|
|
89
|
+
const videoSubdir = getVideoSubdir(options.testFile);
|
|
90
|
+
setup.videoHostDir = path.join(workspacePath, ".skyramp", "videos", videoSubdir);
|
|
91
|
+
setup.videoContainerDir = path.join(containerMountPath, ".skyramp", "videos", videoSubdir);
|
|
92
|
+
if (fs.existsSync(setup.videoHostDir)) {
|
|
93
|
+
fs.rmSync(setup.videoHostDir, { recursive: true, force: true });
|
|
94
|
+
}
|
|
95
|
+
fs.mkdirSync(setup.videoHostDir, { recursive: true });
|
|
96
|
+
// TypeScript/JavaScript: generate a per-execution Playwright config with video.
|
|
97
|
+
if (options.language === "typescript" || options.language === "javascript") {
|
|
98
|
+
const configContent = `import { defineConfig } from "@playwright/test";
|
|
99
|
+
export default defineConfig({
|
|
100
|
+
testDir: ".",
|
|
101
|
+
timeout: 30000,
|
|
102
|
+
retries: 0,
|
|
103
|
+
use: { video: "on" },
|
|
104
|
+
outputDir: "${setup.videoContainerDir}"
|
|
105
|
+
});
|
|
106
|
+
`;
|
|
107
|
+
setup.playwrightConfigPath = path.join(os.tmpdir(), `skyramp-playwright-${videoSubdir}.config.ts`);
|
|
108
|
+
fs.writeFileSync(setup.playwrightConfigPath, configContent);
|
|
109
|
+
}
|
|
110
|
+
// Python: generate a pytest.ini with the video flags.
|
|
111
|
+
if (options.language === "python") {
|
|
112
|
+
const pytestContent = `[pytest]
|
|
113
|
+
addopts = --video on --output ${setup.videoContainerDir}
|
|
114
|
+
`;
|
|
115
|
+
setup.pytestConfigPath = path.join(os.tmpdir(), `skyramp-pytest-${videoSubdir}.ini`);
|
|
116
|
+
fs.writeFileSync(setup.pytestConfigPath, pytestContent);
|
|
117
|
+
}
|
|
118
|
+
return setup;
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* Recursively scan a per-execution video output directory for the recorded
|
|
122
|
+
* `video.webm`. Returns its host path, or undefined if none was produced.
|
|
123
|
+
* Best-effort: scan errors are logged and treated as "no video".
|
|
124
|
+
*/
|
|
125
|
+
function collectVideoPath(videoHostDir) {
|
|
126
|
+
if (!videoHostDir)
|
|
127
|
+
return undefined;
|
|
128
|
+
const findVideo = (dir) => {
|
|
129
|
+
for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
|
|
130
|
+
const fullPath = path.join(dir, entry.name);
|
|
131
|
+
if (entry.isFile() && entry.name === "video.webm")
|
|
132
|
+
return fullPath;
|
|
133
|
+
if (entry.isDirectory()) {
|
|
134
|
+
const found = findVideo(fullPath);
|
|
135
|
+
if (found)
|
|
136
|
+
return found;
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
return undefined;
|
|
140
|
+
};
|
|
141
|
+
try {
|
|
142
|
+
const videoPath = findVideo(videoHostDir);
|
|
143
|
+
if (videoPath) {
|
|
144
|
+
logger.info(`Video recording found: ${videoPath}`);
|
|
145
|
+
}
|
|
146
|
+
else {
|
|
147
|
+
logger.warning(`No video.webm found in ${videoHostDir}`);
|
|
148
|
+
}
|
|
149
|
+
return videoPath;
|
|
150
|
+
}
|
|
151
|
+
catch (err) {
|
|
152
|
+
logger.error(`Failed to scan for video: ${err.message}`);
|
|
153
|
+
return undefined;
|
|
154
|
+
}
|
|
155
|
+
}
|
|
50
156
|
/**
|
|
51
157
|
* Find the start index of a comment in a line, ignoring comment delimiters inside strings
|
|
52
158
|
* Returns -1 if no comment is found outside of strings
|
|
@@ -267,21 +373,20 @@ export class TestExecutionService {
|
|
|
267
373
|
// If execution fails completely, return error result
|
|
268
374
|
return {
|
|
269
375
|
testFile: options.testFile,
|
|
270
|
-
|
|
376
|
+
status: TestExecutionStatus.Error,
|
|
271
377
|
executedAt: new Date().toISOString(),
|
|
272
378
|
duration: 0,
|
|
273
379
|
errors: [error.message],
|
|
274
380
|
warnings: [],
|
|
275
|
-
crashed: true,
|
|
276
381
|
};
|
|
277
382
|
}));
|
|
278
383
|
const batchResults = await Promise.all(batchPromises);
|
|
279
384
|
results.push(...batchResults);
|
|
280
385
|
}
|
|
281
386
|
const totalDuration = Date.now() - startTime;
|
|
282
|
-
const passed = results.filter((r) => r.
|
|
283
|
-
const failed = results.filter((r) =>
|
|
284
|
-
const crashed = results.filter((r) => r.
|
|
387
|
+
const passed = results.filter((r) => r.status === TestExecutionStatus.Pass).length;
|
|
388
|
+
const failed = results.filter((r) => r.status === TestExecutionStatus.Fail).length;
|
|
389
|
+
const crashed = results.filter((r) => r.status === TestExecutionStatus.Error).length;
|
|
285
390
|
logger.info(`Batch execution complete: ${passed} passed, ${failed} failed, ${crashed} crashed`);
|
|
286
391
|
return {
|
|
287
392
|
totalTests: testOptions.length,
|
|
@@ -356,6 +461,8 @@ export class TestExecutionService {
|
|
|
356
461
|
// Calculate relative test file path
|
|
357
462
|
let testFilePath = path.relative(workspacePath, options.testFile);
|
|
358
463
|
testFilePath = path.resolve(containerMountPath, testFilePath);
|
|
464
|
+
// Video recording setup for UI/E2E browser tests (no-op for API tests).
|
|
465
|
+
const { browserTest, videoHostDir, videoContainerDir, playwrightConfigPath, pytestConfigPath } = setupVideoCapture(options, workspacePath, containerMountPath);
|
|
359
466
|
// Prepare Docker command
|
|
360
467
|
const command = [
|
|
361
468
|
"/root/runner.sh",
|
|
@@ -399,6 +506,28 @@ export class TestExecutionService {
|
|
|
399
506
|
mountedPaths.add(mount.Target);
|
|
400
507
|
hostConfig.Mounts.push(mount);
|
|
401
508
|
};
|
|
509
|
+
// For browser tests, mount generated video-enabled configs at canonical container
|
|
510
|
+
// paths BEFORE the nullPaths loop so they WIN over any /dev/null shadows and work
|
|
511
|
+
// even if the repo has no config file. The pushMount dedup ensures later shadows
|
|
512
|
+
// for the same target are dropped.
|
|
513
|
+
if (browserTest) {
|
|
514
|
+
if (options.language === "typescript" || options.language === "javascript") {
|
|
515
|
+
// Mount generated Playwright config at canonical container path
|
|
516
|
+
pushMount({
|
|
517
|
+
Type: "bind",
|
|
518
|
+
Source: playwrightConfigPath,
|
|
519
|
+
Target: path.join(containerMountPath, "playwright.config.ts"),
|
|
520
|
+
});
|
|
521
|
+
}
|
|
522
|
+
if (options.language === "python" && pytestConfigPath) {
|
|
523
|
+
// Mount generated pytest.ini at canonical container path
|
|
524
|
+
pushMount({
|
|
525
|
+
Type: "bind",
|
|
526
|
+
Source: pytestConfigPath,
|
|
527
|
+
Target: path.join(containerMountPath, "pytest.ini"),
|
|
528
|
+
});
|
|
529
|
+
}
|
|
530
|
+
}
|
|
402
531
|
const mirrorAtHostPath = workspacePath !== containerMountPath;
|
|
403
532
|
for (const file of filesToMount) {
|
|
404
533
|
const source = path.join(workspacePath, file);
|
|
@@ -425,23 +554,27 @@ export class TestExecutionService {
|
|
|
425
554
|
pushMount({ Type: "bind", Source: source, Target: absolutePath });
|
|
426
555
|
}
|
|
427
556
|
}
|
|
428
|
-
// Mount Playwright config files with
|
|
429
|
-
//
|
|
557
|
+
// Mount Playwright config files with the per-execution config (minimal or generated).
|
|
558
|
+
// Shadows ANY repo playwright.config.{ts,js,mjs} recursively so repo configs that
|
|
559
|
+
// import dotenv/etc. don't crash the executor. Applies to ALL tests — including
|
|
560
|
+
// non-browser TypeScript tests (contract/integration/fuzz/smoke/load) which per
|
|
561
|
+
// runner.sh also run via `npx playwright test <file>` and read playwright.config.ts.
|
|
430
562
|
const playwrightConfigPaths = findExcludedPaths(workspacePath, PLAYWRIGHT_CONFIG_FILES);
|
|
431
563
|
for (const absolutePath of playwrightConfigPaths) {
|
|
432
564
|
const rel = path.relative(workspacePath, absolutePath);
|
|
565
|
+
pushMount({ Type: "bind", Source: playwrightConfigPath, Target: path.join(containerMountPath, rel) });
|
|
566
|
+
if (mirrorAtHostPath) {
|
|
567
|
+
pushMount({ Type: "bind", Source: playwrightConfigPath, Target: absolutePath });
|
|
568
|
+
}
|
|
569
|
+
}
|
|
570
|
+
// Mount video output directory for browser tests (writable)
|
|
571
|
+
if (browserTest && videoHostDir) {
|
|
433
572
|
pushMount({
|
|
434
573
|
Type: "bind",
|
|
435
|
-
Source:
|
|
436
|
-
Target:
|
|
574
|
+
Source: videoHostDir,
|
|
575
|
+
Target: videoContainerDir,
|
|
576
|
+
ReadOnly: false,
|
|
437
577
|
});
|
|
438
|
-
if (mirrorAtHostPath) {
|
|
439
|
-
pushMount({
|
|
440
|
-
Type: "bind",
|
|
441
|
-
Source: MINIMAL_PLAYWRIGHT_CONFIG_PATH,
|
|
442
|
-
Target: absolutePath,
|
|
443
|
-
});
|
|
444
|
-
}
|
|
445
578
|
}
|
|
446
579
|
// Detect and mount session files referenced outside the workspace
|
|
447
580
|
// (anything inside the workspace is already covered by the dual mount
|
|
@@ -636,28 +769,28 @@ export class TestExecutionService {
|
|
|
636
769
|
// Parse errors and warnings from output
|
|
637
770
|
const errors = this.parseErrors(cleanOutput);
|
|
638
771
|
const warnings = this.parseWarnings(cleanOutput);
|
|
639
|
-
//
|
|
640
|
-
const
|
|
772
|
+
// Collect the recorded video (browser tests only).
|
|
773
|
+
const videoPath = browserTest ? collectVideoPath(videoHostDir) : undefined;
|
|
641
774
|
const result = {
|
|
642
775
|
testFile: options.testFile,
|
|
643
|
-
|
|
776
|
+
status: statusCode === 0 ? TestExecutionStatus.Pass
|
|
777
|
+
: statusCode !== 1 ? TestExecutionStatus.Error
|
|
778
|
+
: TestExecutionStatus.Fail,
|
|
644
779
|
executedAt,
|
|
645
780
|
duration,
|
|
646
781
|
errors,
|
|
647
782
|
warnings,
|
|
648
|
-
crashed,
|
|
649
783
|
output: cleanOutput,
|
|
650
784
|
exitCode: statusCode,
|
|
785
|
+
videoPath,
|
|
651
786
|
};
|
|
652
787
|
// Report completion
|
|
653
788
|
await onProgress?.({
|
|
654
789
|
phase: "processing",
|
|
655
|
-
message: result.
|
|
656
|
-
? "Test passed successfully"
|
|
657
|
-
: "Test execution completed with failures",
|
|
790
|
+
message: `Test execution completed: ${result.status}`,
|
|
658
791
|
percent: 100,
|
|
659
792
|
});
|
|
660
|
-
logger.debug(`Test ${result.
|
|
793
|
+
logger.debug(`Test ${result.status}: ${options.testFile}`);
|
|
661
794
|
return result;
|
|
662
795
|
}
|
|
663
796
|
catch (error) {
|
|
@@ -666,12 +799,11 @@ export class TestExecutionService {
|
|
|
666
799
|
logger.error(`Test execution error: ${error.message}`);
|
|
667
800
|
return {
|
|
668
801
|
testFile: options.testFile,
|
|
669
|
-
|
|
802
|
+
status: TestExecutionStatus.Error,
|
|
670
803
|
executedAt,
|
|
671
804
|
duration,
|
|
672
805
|
errors: [error.message],
|
|
673
806
|
warnings: [],
|
|
674
|
-
crashed: true,
|
|
675
807
|
output: cleanOutput,
|
|
676
808
|
};
|
|
677
809
|
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|