@skyramp/mcp 0.2.6 → 0.2.8-rc.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (248) hide show
  1. package/build/commands/commandLibrary.d.ts +12 -0
  2. package/build/commands/recommendTestsAndExecuteCommand.d.ts +2 -0
  3. package/build/commands/testThisEndpointCommand.d.ts +2 -0
  4. package/build/index.d.ts +2 -0
  5. package/build/index.js +6 -4
  6. package/build/playwright/PlaywrightTraceService.d.ts +32 -0
  7. package/build/playwright/index.d.ts +3 -0
  8. package/build/playwright/registerPlaywrightTools.d.ts +8 -0
  9. package/build/playwright/registerPlaywrightTools.js +12 -11
  10. package/build/playwright/traceRecordingPrompt.d.ts +13 -0
  11. package/build/playwright/traceRecordingPrompt.js +3 -11
  12. package/build/prompts/code-reuse.d.ts +1 -0
  13. package/build/prompts/enhance-assertions/contractProviderAssertionsPrompt.d.ts +2 -0
  14. package/build/prompts/enhance-assertions/integrationAssertionsPrompt.d.ts +2 -0
  15. package/build/prompts/enhance-assertions/sharedAssertionRules.d.ts +15 -0
  16. package/build/prompts/enhance-assertions/uiAssertionsPrompt.d.ts +2 -0
  17. package/build/prompts/enhance-assertions/uiAssertionsPrompt.js +24 -5
  18. package/build/prompts/fix-error-prompt.d.ts +1 -0
  19. package/build/prompts/initialize-workspace/initializeWorkspacePrompt.d.ts +1 -0
  20. package/build/prompts/modularization/integration-test-modularization.d.ts +1 -0
  21. package/build/prompts/modularization/ui-test-modularization.d.ts +1 -0
  22. package/build/prompts/personas.d.ts +21 -0
  23. package/build/prompts/pom-aware-code-reuse.d.ts +1 -0
  24. package/build/prompts/startTraceCollectionPrompts.d.ts +2 -0
  25. package/build/prompts/sut-setup/modes/adaptWorkflowPrompt.d.ts +2 -0
  26. package/build/prompts/sut-setup/modes/adaptWorkflowPrompt.js +114 -0
  27. package/build/prompts/sut-setup/modes/dockerComposePrompt.d.ts +2 -0
  28. package/build/prompts/sut-setup/modes/dockerComposePrompt.js +292 -0
  29. package/build/prompts/sut-setup/shared.d.ts +39 -0
  30. package/build/prompts/sut-setup/shared.js +132 -0
  31. package/build/prompts/test-maintenance/actionsInstructions.d.ts +35 -0
  32. package/build/prompts/test-maintenance/drift-analysis-prompt.d.ts +4 -0
  33. package/build/prompts/test-maintenance/driftAnalysisSections.d.ts +13 -0
  34. package/build/prompts/test-maintenance/driftAnalysisSections.js +9 -3
  35. package/build/prompts/test-recommendation/analysisOutputPrompt.d.ts +84 -0
  36. package/build/prompts/test-recommendation/analysisOutputPrompt.js +1 -1
  37. package/build/prompts/test-recommendation/analysisOutputPrompt.test.d.ts +1 -0
  38. package/build/prompts/test-recommendation/analysisOutputPrompt.test.js +1 -0
  39. package/build/prompts/test-recommendation/diffExecutionPlan.d.ts +17 -0
  40. package/build/prompts/test-recommendation/diffExecutionPlan.js +83 -18
  41. package/build/prompts/test-recommendation/fullRepoCatalog.d.ts +7 -0
  42. package/build/prompts/test-recommendation/mergeEnrichedScenarios.test.d.ts +1 -0
  43. package/build/prompts/test-recommendation/mergeEnrichedScenarios.test.js +1 -0
  44. package/build/prompts/test-recommendation/promptPlan.d.ts +203 -0
  45. package/build/prompts/test-recommendation/promptPlan.test.d.ts +1 -0
  46. package/build/prompts/test-recommendation/recommendationSections.d.ts +35 -0
  47. package/build/prompts/test-recommendation/recommendationShared.d.ts +31 -0
  48. package/build/prompts/test-recommendation/registerRecommendTestsPrompt.d.ts +7 -0
  49. package/build/prompts/test-recommendation/scopeAssessment.d.ts +55 -0
  50. package/build/prompts/test-recommendation/scopeAssessment.js +2 -2
  51. package/build/prompts/test-recommendation/scopeAssessment.test.d.ts +1 -0
  52. package/build/prompts/test-recommendation/scopeAssessment.test.js +1 -0
  53. package/build/prompts/test-recommendation/test-recommendation-prompt.d.ts +6 -0
  54. package/build/prompts/test-recommendation/test-recommendation-prompt.js +4 -4
  55. package/build/prompts/test-recommendation/test-recommendation-prompt.test.d.ts +1 -0
  56. package/build/prompts/test-recommendation/test-recommendation-prompt.test.js +97 -99
  57. package/build/prompts/testbot/testbot-prompts.d.ts +26 -0
  58. package/build/prompts/testbot/testbot-prompts.js +301 -46
  59. package/build/prompts/testbot/testbot-prompts.test.d.ts +1 -0
  60. package/build/prompts/testbot/testbot-prompts.test.js +109 -117
  61. package/build/resources/analysisResources.d.ts +17 -0
  62. package/build/resources/progressResource.d.ts +2 -0
  63. package/build/resources/sutSetupResource.d.ts +2 -0
  64. package/build/resources/sutSetupResource.js +45 -0
  65. package/build/resources/testbotResource.d.ts +2 -0
  66. package/build/resources/testbotResource.js +2 -2
  67. package/build/services/AnalyticsService.d.ts +13 -0
  68. package/build/services/AnalyticsService.test.d.ts +1 -0
  69. package/build/services/AnalyticsService.test.js +86 -0
  70. package/build/services/ModularizationService.d.ts +11 -0
  71. package/build/services/ScenarioGenerationService.d.ts +37 -0
  72. package/build/services/ScenarioGenerationService.integration.test.d.ts +1 -0
  73. package/build/services/ScenarioGenerationService.integration.test.js +4 -0
  74. package/build/services/ScenarioGenerationService.test.d.ts +1 -0
  75. package/build/services/TestDiscoveryService.d.ts +128 -0
  76. package/build/services/TestDiscoveryService.js +22 -12
  77. package/build/services/TestDiscoveryService.test.d.ts +1 -0
  78. package/build/services/TestDiscoveryService.test.js +4 -3
  79. package/build/services/TestExecutionService.d.ts +55 -0
  80. package/build/services/TestExecutionService.js +158 -26
  81. package/build/services/TestExecutionService.test.d.ts +1 -0
  82. package/build/services/TestExecutionService.test.js +339 -25
  83. package/build/services/TestGenerationService.d.ts +56 -0
  84. package/build/services/TestGenerationService.test.d.ts +1 -0
  85. package/build/services/TestGenerationService.test.js +10 -8
  86. package/build/services/containerEnv.d.ts +14 -0
  87. package/build/tool-phase-coverage.test.d.ts +1 -0
  88. package/build/tool-phase-coverage.test.js +2 -0
  89. package/build/tool-phases.d.ts +38 -0
  90. package/build/tools/auth/loginTool.d.ts +2 -0
  91. package/build/tools/auth/logoutTool.d.ts +2 -0
  92. package/build/tools/code-refactor/codeReuseTool.d.ts +2 -0
  93. package/build/tools/code-refactor/enhanceAssertionsTool.d.ts +2 -0
  94. package/build/tools/code-refactor/modularizationTool.d.ts +2 -0
  95. package/build/tools/executeSkyrampTestTool.d.ts +2 -0
  96. package/build/tools/executeSkyrampTestTool.js +26 -7
  97. package/build/tools/fixErrorTool.d.ts +2 -0
  98. package/build/tools/generate-tests/generateBatchScenarioRestTool.d.ts +45 -0
  99. package/build/tools/generate-tests/generateBatchScenarioRestTool.js +1 -1
  100. package/build/tools/generate-tests/generateBatchScenarioRestTool.test.d.ts +1 -0
  101. package/build/tools/generate-tests/generateBatchScenarioRestTool.test.js +11 -9
  102. package/build/tools/generate-tests/generateContractRestTool.d.ts +31 -0
  103. package/build/tools/generate-tests/generateE2ERestTool.d.ts +13 -0
  104. package/build/tools/generate-tests/generateFuzzRestTool.d.ts +10 -0
  105. package/build/tools/generate-tests/generateIntegrationRestTool.d.ts +16 -0
  106. package/build/tools/generate-tests/generateLoadRestTool.d.ts +18 -0
  107. package/build/tools/generate-tests/generateLoadRestTool.test.d.ts +1 -0
  108. package/build/tools/generate-tests/generateMockRestTool.d.ts +64 -0
  109. package/build/tools/generate-tests/generateSmokeRestTool.d.ts +8 -0
  110. package/build/tools/generate-tests/generateUIRestTool.d.ts +14 -0
  111. package/build/tools/generate-tests/generateUIRestTool.js +1 -1
  112. package/build/tools/generate-tests/loadTestSchema.d.ts +39 -0
  113. package/build/tools/one-click/oneClickTool.d.ts +11 -0
  114. package/build/tools/submitReportTool.d.ts +394 -0
  115. package/build/tools/submitReportTool.js +250 -41
  116. package/build/tools/submitReportTool.test.d.ts +1 -0
  117. package/build/tools/submitReportTool.test.js +511 -55
  118. package/build/tools/test-management/actionsTool.d.ts +10 -0
  119. package/build/tools/test-management/actionsTool.js +21 -7
  120. package/build/tools/test-management/analyzeChangesTool.d.ts +27 -0
  121. package/build/tools/test-management/analyzeChangesTool.js +150 -48
  122. package/build/tools/test-management/analyzeChangesTool.test.d.ts +1 -0
  123. package/build/tools/test-management/analyzeChangesTool.test.js +95 -78
  124. package/build/tools/test-management/analyzeTestHealthTool.d.ts +2 -0
  125. package/build/tools/test-management/analyzeTestHealthTool.js +16 -9
  126. package/build/tools/test-management/index.d.ts +3 -0
  127. package/build/tools/test-management/index.js +0 -1
  128. package/build/tools/trace/resolveSaveStoragePath.d.ts +7 -0
  129. package/build/tools/trace/resolveSaveStoragePath.test.d.ts +1 -0
  130. package/build/tools/trace/resolveSessionPaths.d.ts +40 -0
  131. package/build/tools/trace/resolveSessionPaths.test.d.ts +1 -0
  132. package/build/tools/trace/sessionState.d.ts +2 -0
  133. package/build/tools/trace/sessionState.test.d.ts +1 -0
  134. package/build/tools/trace/startTraceCollectionTool.d.ts +2 -0
  135. package/build/tools/trace/stopTraceCollectionTool.d.ts +2 -0
  136. package/build/tools/workspace/initScanWorkspaceTool.d.ts +4 -0
  137. package/build/tools/workspace/initializeWorkspaceTool.d.ts +2 -0
  138. package/build/tools/workspace/initializeWorkspaceTool.js +39 -22
  139. package/build/tools/workspace/initializeWorkspaceTool.test.d.ts +1 -0
  140. package/build/tools/workspace/initializeWorkspaceTool.test.js +134 -0
  141. package/build/tools/workspace/serviceUpsert.d.ts +12 -0
  142. package/build/tools/workspace/serviceUpsert.js +23 -0
  143. package/build/tools/workspace/serviceUpsert.test.d.ts +1 -0
  144. package/build/tools/workspace/serviceUpsert.test.js +50 -0
  145. package/build/types/OneClickCommands.d.ts +68 -0
  146. package/build/types/RepositoryAnalysis.d.ts +3004 -0
  147. package/build/types/TestAnalysis.d.ts +97 -0
  148. package/build/types/TestExecution.d.ts +63 -0
  149. package/build/types/TestExecution.js +11 -1
  150. package/build/types/TestRecommendation.d.ts +12 -0
  151. package/build/types/TestTypes.d.ts +234 -0
  152. package/build/types/TestbotReport.d.ts +68 -0
  153. package/build/types/TestbotReport.js +1 -0
  154. package/build/types/index.d.ts +4 -0
  155. package/build/types/index.js +3 -0
  156. package/build/utils/AnalysisStateManager.d.ts +242 -0
  157. package/build/utils/AnalysisStateManager.js +144 -3
  158. package/build/utils/AnalysisStateManager.test.d.ts +1 -0
  159. package/build/utils/AnalysisStateManager.test.js +98 -0
  160. package/build/utils/analyze-openapi.d.ts +1 -0
  161. package/build/utils/branchDiff.d.ts +18 -0
  162. package/build/utils/dartRouteExtractor.d.ts +45 -0
  163. package/build/utils/dartRouteExtractor.test.d.ts +1 -0
  164. package/build/utils/docker.d.ts +19 -0
  165. package/build/utils/docker.test.d.ts +1 -0
  166. package/build/utils/docker.test.js +10 -9
  167. package/build/utils/featureFlags.d.ts +37 -0
  168. package/build/utils/featureFlags.test.d.ts +1 -0
  169. package/build/utils/gitStaging.d.ts +24 -0
  170. package/build/utils/gitStaging.test.d.ts +1 -0
  171. package/build/utils/gitStaging.test.js +13 -7
  172. package/build/utils/httpDefaults.d.ts +10 -0
  173. package/build/utils/httpDefaults.test.d.ts +1 -0
  174. package/build/utils/initAgent.d.ts +36 -0
  175. package/build/utils/language-helper.d.ts +6 -0
  176. package/build/utils/logger.d.ts +11 -0
  177. package/build/utils/normalizeParams.d.ts +14 -0
  178. package/build/utils/normalizeSkyrampImports.d.ts +13 -0
  179. package/build/utils/pr-comment-parser.d.ts +31 -0
  180. package/build/utils/pr-comment-parser.test.d.ts +1 -0
  181. package/build/utils/pr-comment-parser.test.js +6 -5
  182. package/build/utils/projectMetadata.d.ts +15 -0
  183. package/build/utils/projectMetadata.test.d.ts +1 -0
  184. package/build/utils/proxy-terminal.d.ts +2 -0
  185. package/build/utils/repoScanner.d.ts +27 -0
  186. package/build/utils/repoScanner.test.d.ts +1 -0
  187. package/build/utils/routeParsers.d.ts +53 -0
  188. package/build/utils/routeParsers.js +31 -38
  189. package/build/utils/routeParsers.test.d.ts +1 -0
  190. package/build/utils/scenarioDrafting.d.ts +71 -0
  191. package/build/utils/scenarioDrafting.js +23 -1
  192. package/build/utils/scenarioDrafting.test.d.ts +1 -0
  193. package/build/utils/skyrampMdContent.d.ts +5 -0
  194. package/build/utils/sourceRouteExtractor.d.ts +48 -0
  195. package/build/utils/sourceRouteExtractor.test.d.ts +1 -0
  196. package/build/utils/telemetry.d.ts +32 -0
  197. package/build/utils/telemetry.test.d.ts +1 -0
  198. package/build/utils/trace-parser.d.ts +32 -0
  199. package/build/utils/trace-parser.test.d.ts +6 -0
  200. package/build/utils/uiPageEnumerator.d.ts +146 -0
  201. package/build/utils/uiPageEnumerator.js +58 -14
  202. package/build/utils/uiPageEnumerator.test.d.ts +1 -0
  203. package/build/utils/uiPageEnumerator.test.js +223 -43
  204. package/build/utils/utils.d.ts +45 -0
  205. package/build/utils/utils.js +10 -0
  206. package/build/utils/utils.test.d.ts +1 -0
  207. package/build/utils/versions.d.ts +3 -0
  208. package/build/utils/versions.js +1 -1
  209. package/build/utils/workspaceAuth.d.ts +127 -0
  210. package/build/utils/workspaceAuth.test.d.ts +1 -0
  211. package/build/utils/workspaceAuth.test.js +25 -25
  212. package/build/workspace/index.d.ts +1 -0
  213. package/build/workspace/workspace.d.ts +390 -0
  214. package/build/workspace/workspace.js +7 -7
  215. package/build/workspace/workspace.test.d.ts +1 -0
  216. package/build/workspace/workspace.test.js +6 -6
  217. package/node_modules/playwright/lib/mcp/skyramp/loadTraceTool.js +31 -13
  218. package/node_modules/playwright/lib/mcp/skyramp/traceRecordingBackend.js +88 -15
  219. package/node_modules/playwright/lib/mcp/test/skyRampExport.js +4 -3
  220. package/node_modules/playwright/node_modules/playwright-core/ThirdPartyNotices.txt +23 -126
  221. package/node_modules/playwright/node_modules/playwright-core/lib/generated/injectedScriptSource.js +1 -1
  222. package/node_modules/playwright/node_modules/playwright-core/lib/server/recorder/recorderApp.js +79 -19
  223. package/node_modules/playwright/node_modules/playwright-core/lib/server/recorder/skyramp/aliasRemapper.js +84 -0
  224. package/node_modules/playwright/node_modules/playwright-core/lib/server/recorder/skyramp/replayEngine.js +15 -4
  225. package/node_modules/playwright/node_modules/playwright-core/lib/vite/recorder/assets/{codeMirrorModule-CPZKmjFR.js → codeMirrorModule-D0BjbCb7.js} +1 -1
  226. package/node_modules/playwright/node_modules/playwright-core/lib/vite/recorder/assets/index-lvTRGFx-.js +193 -0
  227. package/node_modules/playwright/node_modules/playwright-core/lib/vite/recorder/index.html +1 -1
  228. package/node_modules/playwright/node_modules/playwright-core/lib/vite/traceViewer/assets/{codeMirrorModule-B0JOjboO.js → codeMirrorModule-Cqp3cwEJ.js} +1 -1
  229. package/node_modules/playwright/node_modules/playwright-core/lib/vite/traceViewer/assets/{defaultSettingsView-1anWeyDf.js → defaultSettingsView-CYf9adZh.js} +79 -79
  230. package/node_modules/playwright/node_modules/playwright-core/lib/vite/traceViewer/{index.Cc5029a3.js → index.B_7ywgmr.js} +1 -1
  231. package/node_modules/playwright/node_modules/playwright-core/lib/vite/traceViewer/index.html +2 -2
  232. package/node_modules/playwright/node_modules/playwright-core/lib/vite/traceViewer/uiMode.html +2 -2
  233. package/node_modules/playwright/node_modules/playwright-core/lib/vite/traceViewer/{uiMode.Wo5yvvVh.js → uiMode.u4_8VnCV.js} +1 -1
  234. package/node_modules/playwright/node_modules/playwright-core/package.json +1 -1
  235. package/node_modules/playwright/node_modules/playwright-core/src/generated/injectedScriptSource.ts +1 -1
  236. package/node_modules/playwright/node_modules/playwright-core/src/server/recorder/recorderApp.ts +129 -43
  237. package/node_modules/playwright/node_modules/playwright-core/src/server/recorder/skyramp/aliasRemapper.ts +119 -0
  238. package/node_modules/playwright/node_modules/playwright-core/src/server/recorder/skyramp/replayEngine.ts +33 -10
  239. package/node_modules/playwright/package.json +1 -1
  240. package/package.json +16 -4
  241. package/build/tools/test-management/uiAnalyzeChangesTool.js +0 -155
  242. package/build/tools/test-management/uiAnalyzeChangesTool.test.js +0 -147
  243. package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/.bin/node-which +0 -52
  244. package/node_modules/playwright/node_modules/playwright-core/bundles/utils/node_modules/.bin/is-docker +0 -5
  245. package/node_modules/playwright/node_modules/playwright-core/bundles/utils/node_modules/.bin/mime +0 -46
  246. package/node_modules/playwright/node_modules/playwright-core/bundles/utils/node_modules/.bin/yaml +0 -11
  247. package/node_modules/playwright/node_modules/playwright-core/lib/vite/recorder/assets/index-CsgPR2wy.js +0 -193
  248. package/node_modules/playwright/node_modules/playwright-core/lib/vite/traceViewer/index.CP3Y8Zsb.js +0 -2
@@ -0,0 +1,128 @@
1
+ import { TestAnalysisResult } from "../types/TestAnalysis.js";
2
+ interface TestDiscoveryResult {
3
+ tests: TestAnalysisResult[];
4
+ /** Paths of external files that were deemed relevant to the PR (score > 0). */
5
+ relevantExternalTestPaths: string[];
6
+ }
7
+ export interface TestDiscoveryOptions {
8
+ /**
9
+ * Resource names derived from endpoints in changed files (e.g. ["orders", "products"]).
10
+ * - Non-empty array: external files partitioned by relevance; only relevant files
11
+ * get full endpoint extraction. May be the sentinel `["unknown"]` when endpoints
12
+ * exist but resource names are unresolvable — treated as "endpoints present" to
13
+ * avoid skipping external coverage. Also used for "add field" PRs where the changed
14
+ * files have no endpoint definitions but the scanner found related endpoints nearby.
15
+ * - Empty array `[]`: PR mode with no endpoints from diff or scanner — external
16
+ * tests are skipped entirely to avoid flooding context.
17
+ * - `undefined`: full-repo mode — external tests capped at MAX_EXTERNAL_FULL_REPO.
18
+ */
19
+ changedResources?: string[];
20
+ }
21
+ export declare class TestDiscoveryService {
22
+ private readonly EXCLUDED_DIRS;
23
+ private readonly SKYRAMP_MARKER;
24
+ private readonly SUPPORTED_EXTENSIONS;
25
+ private readonly MAX_CONCURRENT_OPERATIONS;
26
+ private readonly MAX_EXTERNAL_FULL_REPO;
27
+ private readonly TEST_FILE_PATTERNS;
28
+ private readonly TEST_DIR_PATTERNS;
29
+ /**
30
+ * Discover all tests under testDir — both Skyramp-generated and external (user-written).
31
+ * Uses fast-glob for cross-platform file scanning, then classifies discovered files
32
+ * as Skyramp-generated tests, external tests, or not-a-test during processing.
33
+ *
34
+ * External test handling depends on `options.changedResources`:
35
+ * - `string[]` with entries (PR mode, endpoints detected): partition by relevance.
36
+ * - `[]` empty array (PR mode, scanner found no endpoints): skip external tests entirely
37
+ * rather than flooding context with irrelevant files.
38
+ * - `undefined` (full-repo mode, no diff): cap at MAX_EXTERNAL_FULL_REPO.
39
+ */
40
+ discoverTests(testDir: string, options?: TestDiscoveryOptions): Promise<TestDiscoveryResult>;
41
+ /**
42
+ * Score an external test file's relevance to a set of changed resource names.
43
+ * Tokenises the last two segments of the file path (split by /, \, -, _, .)
44
+ * and counts overlapping tokens with the changed resources.
45
+ * Example: "test_orders_api.py" vs ["orders"] → score 1.
46
+ */
47
+ private scoreRelevance;
48
+ /**
49
+ * Partition external test files into relevant (score > 0) and low-relevance (score = 0).
50
+ * Signal: path/name token overlap with changedResources.
51
+ */
52
+ private partitionByRelevance;
53
+ /**
54
+ * Process test files in parallel batches with concurrency control
55
+ * @param isExternal When true, uses external test metadata extraction
56
+ * @param contentCache Optional pre-read file contents from classification pass
57
+ */
58
+ private processFilesInBatches;
59
+ /**
60
+ * Classify candidate files as skyramp (has marker), external (test file without
61
+ * marker), or not-a-test. Caches file contents so downstream metadata extraction
62
+ * doesn't need to re-read from disk.
63
+ */
64
+ private classifyTestFiles;
65
+ /**
66
+ * Check if a file is an external (non-Skyramp) test file based on naming convention
67
+ * or directory placement.
68
+ */
69
+ private isExternalTestFile;
70
+ /**
71
+ * Extract metadata from a test file
72
+ * File is already confirmed to contain Skyramp marker by file search
73
+ * @param cachedContent Optional pre-read content from classification pass
74
+ */
75
+ private extractTestMetadata;
76
+ /**
77
+ * Extract the HTTP methods and paths covered by this test file.
78
+ * Returns a comma-separated string like "GET /orders/{id}, DELETE /orders/{id}".
79
+ * Handles Python (send_request / check_schema) and TypeScript/JS (sendRequest).
80
+ */
81
+ private extractCoveredEndpoints;
82
+ /**
83
+ * Extract metadata from an external (non-Skyramp) test file.
84
+ * Uses heuristic patterns for endpoint extraction, test type, and framework detection.
85
+ * @param cachedContent Optional pre-read content from classification pass
86
+ */
87
+ private extractExternalTestMetadata;
88
+ /**
89
+ * Extract HTTP endpoints from external (non-Skyramp) test files.
90
+ * Matches common patterns: requests.get, axios.post, fetch, supertest, RestAssured.
91
+ */
92
+ private extractExternalEndpoints;
93
+ /**
94
+ * Normalize an endpoint path extracted from test code.
95
+ * Strips base URL prefixes and returns only the path portion.
96
+ * Returns null if the path doesn't look like an API endpoint.
97
+ */
98
+ private normalizeEndpointPath;
99
+ /**
100
+ * Detect test type from external test file path and content.
101
+ * Falls back to directory/filename heuristics since external tests lack Skyramp command lines.
102
+ */
103
+ private detectExternalTestType;
104
+ /**
105
+ * Detect test framework from external test file content by checking imports.
106
+ */
107
+ private detectExternalFramework;
108
+ /**
109
+ * Detect programming language from file extension
110
+ */
111
+ private detectLanguage;
112
+ /**
113
+ * Detect test type from file content and name
114
+ * Checks Skyramp command line, filename, and content patterns
115
+ */
116
+ private detectTestType;
117
+ /**
118
+ * Extract API schema path from test content
119
+ * Looks for Skyramp command line first, then fallback to other patterns
120
+ */
121
+ private extractApiSchema;
122
+ /**
123
+ * Extract Framework from test content
124
+ * Looks for Skyramp command line first, then fallback to other patterns
125
+ */
126
+ private extractFramework;
127
+ }
128
+ export {};
@@ -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 an explicit empty endpoint list from diff parsing — don't flood
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
- // Direct single-token match (e.g. "orders" in file path tokens)
157
- if (tokens.has(rLower))
164
+ if (expanded.has(rLower))
158
165
  return true;
159
- // Compound resource names (e.g. "order-items" from /api/order-items) — check
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 && tokens.has(p));
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
- * based on file path/name overlap with the changed resource names from the PR diff.
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 ? relevant : other).push(f);
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.mock("../utils/logger.js", () => ({
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.29";
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
- passed: false,
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.passed).length;
283
- const failed = results.filter((r) => !r.passed && !r.crashed).length;
284
- const crashed = results.filter((r) => r.crashed).length;
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 minimal config (shadows repo configs that may
429
- // import dotenv or other dependencies not available in the executor container)
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: MINIMAL_PLAYWRIGHT_CONFIG_PATH,
436
- Target: path.join(containerMountPath, rel),
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
- // Check if test crashed
640
- const crashed = statusCode !== 0 && statusCode !== 1;
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
- passed: statusCode === 0,
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.passed
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.passed ? "passed" : "failed"}: ${options.testFile}`);
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
- passed: false,
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 {};