@skyramp/mcp 0.3.2 → 0.3.4

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 (181) hide show
  1. package/build/index.js +47 -2
  2. package/build/playwright/PlaywrightTraceService.d.ts +8 -0
  3. package/build/playwright/PlaywrightTraceService.js +1 -0
  4. package/build/playwright/registerPlaywrightTools.js +42 -1
  5. package/build/prompts/enhance-assertions/sharedAssertionRules.js +19 -0
  6. package/build/prompts/pom-aware-code-reuse.js +17 -8
  7. package/build/prompts/test-maintenance/actionsInstructions.js +2 -2
  8. package/build/prompts/test-recommendation/analysisOutputPrompt.js +1 -4
  9. package/build/prompts/test-recommendation/recommendationSections.d.ts +1 -1
  10. package/build/prompts/test-recommendation/recommendationSections.js +5 -5
  11. package/build/prompts/test-recommendation/test-recommendation-prompt.js +13 -7
  12. package/build/prompts/testbot/testbot-prompts.d.ts +2 -12
  13. package/build/prompts/testbot/testbot-prompts.js +28 -16
  14. package/build/recommendation/discriminators.d.ts +7 -1
  15. package/build/recommendation/discriminators.js +16 -3
  16. package/build/resources/testbotResource.js +20 -4
  17. package/build/services/ScenarioGenerationService.js +5 -2
  18. package/build/services/TestExecutionService.d.ts +13 -8
  19. package/build/services/TestExecutionService.js +73 -26
  20. package/build/services/TestGenerationService.js +24 -9
  21. package/build/services/containerEnv.d.ts +12 -1
  22. package/build/services/containerEnv.js +118 -1
  23. package/build/tools/executeSkyrampTestTool.d.ts +9 -0
  24. package/build/tools/executeSkyrampTestTool.js +20 -6
  25. package/build/tools/execution-video-state.d.ts +21 -0
  26. package/build/tools/execution-video-state.js +51 -0
  27. package/build/tools/generate-tests/generateBatchScenarioRestTool.js +31 -11
  28. package/build/tools/generate-tests/planGuard.d.ts +5 -5
  29. package/build/tools/generate-tests/planGuard.js +5 -17
  30. package/build/tools/queryProxyMocksTool.js +0 -1
  31. package/build/tools/submitReportTool.d.ts +83 -10
  32. package/build/tools/submitReportTool.js +179 -29
  33. package/build/tools/test-management/actionsTool.js +52 -41
  34. package/build/tools/test-management/analyzeChangesTool.d.ts +11 -0
  35. package/build/tools/test-management/analyzeChangesTool.js +37 -33
  36. package/build/tools/test-management/analyzeTestHealthTool.js +3 -3
  37. package/build/tools/test-management/registerTestPlanTool.js +113 -31
  38. package/build/types/TestAnalysis.d.ts +7 -3
  39. package/build/types/TestExecution.d.ts +14 -0
  40. package/build/types/TestTypes.js +3 -2
  41. package/build/types/TestbotPromptOptions.d.ts +34 -0
  42. package/build/types/TestbotPromptOptions.js +1 -0
  43. package/build/types/TestbotReport.d.ts +10 -0
  44. package/build/types/TestbotReport.js +10 -1
  45. package/build/types/index.d.ts +2 -0
  46. package/build/types/index.js +1 -0
  47. package/build/utils/AnalysisStateManager.d.ts +36 -2
  48. package/build/utils/AnalysisStateManager.js +34 -13
  49. package/build/utils/frontendSelectors.js +0 -1
  50. package/build/utils/gitStaging.d.ts +5 -0
  51. package/build/utils/gitStaging.js +1 -1
  52. package/build/utils/pom-catalog.d.ts +23 -0
  53. package/build/utils/pom-catalog.js +30 -0
  54. package/build/utils/pom-scope/pom-files.d.ts +14 -0
  55. package/build/utils/pom-scope/pom-files.js +32 -6
  56. package/build/utils/pom-scope/testIdDiscovery.d.ts +40 -0
  57. package/build/utils/pom-scope/testIdDiscovery.js +104 -0
  58. package/build/utils/reportVerification.d.ts +7 -2
  59. package/build/utils/reportVerification.js +9 -3
  60. package/build/utils/scenarioDrafting.js +7 -1
  61. package/build/utils/skyrampMdContent.d.ts +1 -1
  62. package/build/utils/skyrampMdContent.js +1 -1
  63. package/build/utils/urlPath.d.ts +37 -0
  64. package/build/utils/urlPath.js +55 -0
  65. package/build/utils/utils.d.ts +45 -0
  66. package/build/utils/utils.js +50 -0
  67. package/build/utils/versions.d.ts +3 -3
  68. package/build/utils/versions.js +1 -1
  69. package/build/utils/workspaceAuth.d.ts +15 -15
  70. package/build/utils/workspaceAuth.js +32 -17
  71. package/build/workspace/queryParamResolution.d.ts +93 -0
  72. package/build/workspace/queryParamResolution.js +201 -0
  73. package/build/workspace/workspace.d.ts +104 -0
  74. package/build/workspace/workspace.js +24 -0
  75. package/node_modules/playwright/ThirdPartyNotices.txt +319 -266
  76. package/node_modules/playwright/lib/dom-analyzer/blueprint.js +154 -27
  77. package/node_modules/playwright/lib/dom-analyzer/crawler.js +2 -2
  78. package/node_modules/playwright/lib/mcp/browser/tools/pageBlueprint.js +1 -1
  79. package/node_modules/playwright/lib/mcp/browser/tools/sitemap.js +6 -2
  80. package/node_modules/playwright/lib/mcp/skyramp/loadTraceTool.js +3 -2
  81. package/node_modules/playwright/lib/mcp/skyramp/resultCode.js +4 -3
  82. package/node_modules/playwright/lib/mcp/skyramp/skyRampImport.js +15 -3
  83. package/node_modules/playwright/lib/mcp/skyramp/specImport.js +781 -0
  84. package/node_modules/playwright/lib/mcp/skyramp/traceRecordingBackend.js +14 -3
  85. package/node_modules/playwright/lib/mcp/test/resultCode.test.js +2 -1
  86. package/node_modules/playwright/lib/mcp/test/skyRampExport.js +1 -1
  87. package/node_modules/playwright/lib/mcp/test/skyRampExport.test.js +30 -0
  88. package/node_modules/playwright/lib/transform/babelBundleImpl.js +200 -199
  89. package/node_modules/playwright/node_modules/playwright-core/ThirdPartyNotices.txt +3 -3
  90. package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/.package-lock.json +3 -3
  91. package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/adapter/aws-lambda/handler.js +16 -25
  92. package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/adapter/bun/websocket.js +3 -1
  93. package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/adapter/lambda-edge/handler.js +20 -4
  94. package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/cjs/adapter/aws-lambda/handler.js +16 -25
  95. package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/cjs/adapter/bun/websocket.js +3 -1
  96. package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/cjs/adapter/lambda-edge/handler.js +20 -4
  97. package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/cjs/client/client.js +10 -1
  98. package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/cjs/client/utils.js +1 -1
  99. package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/cjs/helper/css/common.js +3 -1
  100. package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/cjs/helper/css/index.js +9 -1
  101. package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/cjs/jsx/base.js +8 -14
  102. package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/cjs/jsx/components.js +41 -21
  103. package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/cjs/jsx/context.js +131 -5
  104. package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/cjs/jsx/streaming.js +9 -7
  105. package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/cjs/middleware/bearer-auth/index.js +1 -1
  106. package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/cjs/middleware/cache/index.js +1 -1
  107. package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/cjs/middleware/compress/index.js +2 -1
  108. package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/cjs/middleware/cors/index.js +2 -5
  109. package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/cjs/middleware/etag/index.js +2 -1
  110. package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/cjs/middleware/language/language.js +10 -32
  111. package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/cjs/middleware/method-override/index.js +5 -3
  112. package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/cjs/middleware/serve-static/index.js +2 -2
  113. package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/cjs/middleware/timing/timing.js +3 -1
  114. package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/cjs/router/trie-router/node.js +9 -0
  115. package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/cjs/utils/body.js +12 -4
  116. package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/cjs/utils/buffer.js +2 -1
  117. package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/cjs/utils/ipaddr.js +6 -1
  118. package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/cjs/validator/validator.js +3 -3
  119. package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/client/client.js +10 -1
  120. package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/client/utils.js +1 -1
  121. package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/helper/css/common.js +3 -1
  122. package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/helper/css/index.js +9 -1
  123. package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/jsx/base.js +15 -15
  124. package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/jsx/components.js +42 -22
  125. package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/jsx/context.js +129 -5
  126. package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/jsx/streaming.js +10 -8
  127. package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/middleware/bearer-auth/index.js +1 -1
  128. package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/middleware/cache/index.js +1 -1
  129. package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/middleware/compress/index.js +2 -1
  130. package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/middleware/cors/index.js +2 -5
  131. package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/middleware/etag/index.js +2 -1
  132. package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/middleware/language/language.js +10 -32
  133. package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/middleware/method-override/index.js +5 -3
  134. package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/middleware/serve-static/index.js +2 -2
  135. package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/middleware/timing/timing.js +3 -1
  136. package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/router/trie-router/node.js +9 -0
  137. package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/types/adapter/aws-lambda/handler.d.ts +1 -1
  138. package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/types/adapter/lambda-edge/handler.d.ts +1 -1
  139. package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/types/helper/websocket/index.d.ts +1 -1
  140. package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/types/jsx/base.d.ts +1 -3
  141. package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/types/jsx/context.d.ts +39 -0
  142. package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/types/middleware/context-storage/index.d.ts +2 -2
  143. package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/types/middleware/language/language.d.ts +18 -0
  144. package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/types/utils/body.d.ts +1 -1
  145. package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/types/utils/types.d.ts +1 -1
  146. package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/utils/body.js +12 -4
  147. package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/utils/buffer.js +2 -1
  148. package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/utils/ipaddr.js +6 -1
  149. package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/validator/validator.js +3 -3
  150. package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/package.json +29 -22
  151. package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/package-lock.json +3 -3
  152. package/node_modules/playwright/node_modules/playwright-core/lib/generated/pollingRecorderSource.js +1 -1
  153. package/node_modules/playwright/node_modules/playwright-core/lib/server/codegen/skyramp/jsonlReader.js +5 -1
  154. package/node_modules/playwright/node_modules/playwright-core/lib/server/codegen/skyramp/specReader.js +781 -0
  155. package/node_modules/playwright/node_modules/playwright-core/lib/server/recorder/recorderApp.js +25 -6
  156. package/node_modules/playwright/node_modules/playwright-core/lib/server/recorder/skyramp/replayEngine.js +3 -1
  157. package/node_modules/playwright/node_modules/playwright-core/lib/utils.js +2 -0
  158. package/node_modules/playwright/node_modules/playwright-core/lib/vite/htmlReport/index.html +253 -27
  159. package/node_modules/playwright/node_modules/playwright-core/lib/vite/recorder/assets/{codeMirrorModule-D0BjbCb7.js → codeMirrorModule-DtudTj_v.js} +1 -1
  160. package/node_modules/playwright/node_modules/playwright-core/lib/vite/recorder/assets/index-BpDwp16L.js +422 -0
  161. package/node_modules/playwright/node_modules/playwright-core/lib/vite/recorder/index.html +1 -1
  162. package/node_modules/playwright/node_modules/playwright-core/lib/vite/traceViewer/assets/{codeMirrorModule-Bzd72-bG.js → codeMirrorModule-FNMuBzX1.js} +1 -1
  163. package/node_modules/playwright/node_modules/playwright-core/lib/vite/traceViewer/assets/defaultSettingsView-Co9upU5h.js +1035 -0
  164. package/node_modules/playwright/node_modules/playwright-core/lib/vite/traceViewer/index.DXNIQ_dx.js +2 -0
  165. package/node_modules/playwright/node_modules/playwright-core/lib/vite/traceViewer/index.html +2 -2
  166. package/node_modules/playwright/node_modules/playwright-core/lib/vite/traceViewer/uiMode.CIKB3XSv.js +5 -0
  167. package/node_modules/playwright/node_modules/playwright-core/lib/vite/traceViewer/uiMode.html +2 -2
  168. package/node_modules/playwright/node_modules/playwright-core/package.json +1 -1
  169. package/node_modules/playwright/node_modules/playwright-core/src/generated/pollingRecorderSource.ts +1 -1
  170. package/node_modules/playwright/node_modules/playwright-core/src/server/codegen/skyramp/jsonlReader.ts +4 -0
  171. package/node_modules/playwright/node_modules/playwright-core/src/server/codegen/skyramp/specReader.ts +1028 -0
  172. package/node_modules/playwright/node_modules/playwright-core/src/server/recorder/recorderApp.ts +31 -8
  173. package/node_modules/playwright/node_modules/playwright-core/src/server/recorder/skyramp/replayEngine.ts +1 -0
  174. package/node_modules/playwright/node_modules/playwright-core/src/utils.ts +1 -0
  175. package/node_modules/playwright/package.json +1 -1
  176. package/package.json +10 -6
  177. package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/tsconfig.build.tsbuildinfo +0 -1
  178. package/node_modules/playwright/node_modules/playwright-core/lib/vite/recorder/assets/index-lvTRGFx-.js +0 -193
  179. package/node_modules/playwright/node_modules/playwright-core/lib/vite/traceViewer/assets/defaultSettingsView-DzxTioTK.js +0 -809
  180. package/node_modules/playwright/node_modules/playwright-core/lib/vite/traceViewer/index.BGc30U3S.js +0 -2
  181. package/node_modules/playwright/node_modules/playwright-core/lib/vite/traceViewer/uiMode.IaDrb29A.js +0 -5
@@ -19,6 +19,7 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
19
19
  var blueprint_exports = {};
20
20
  __export(blueprint_exports, {
21
21
  BlueprintInvariantError: () => BlueprintInvariantError,
22
+ DEFAULT_TEST_ID_ATTRIBUTE: () => DEFAULT_TEST_ID_ATTRIBUTE,
22
23
  applyPositionHintToNamelessElements: () => applyPositionHintToNamelessElements,
23
24
  buildMap: () => buildMap,
24
25
  buildOutline: () => buildOutline,
@@ -26,6 +27,7 @@ __export(blueprint_exports, {
26
27
  deriveLogicalName: () => deriveLogicalName,
27
28
  resolveAccessibleName: () => resolveAccessibleName,
28
29
  resolveTemplate: () => resolveTemplate,
30
+ resolveTestIdAttributes: () => resolveTestIdAttributes,
29
31
  resolveXpath: () => resolveXpath,
30
32
  validateElement: () => validateElement,
31
33
  validateRepeatingElement: () => validateRepeatingElement,
@@ -42,6 +44,13 @@ class BlueprintInvariantError extends Error {
42
44
  this.name = "BlueprintInvariantError";
43
45
  }
44
46
  }
47
+ const DEFAULT_TEST_ID_ATTRIBUTE = "data-testid";
48
+ function resolveTestIdAttributes(configured) {
49
+ const trimmed = configured?.trim();
50
+ if (!trimmed || trimmed === DEFAULT_TEST_ID_ATTRIBUTE)
51
+ return [DEFAULT_TEST_ID_ATTRIBUTE];
52
+ return [trimmed, DEFAULT_TEST_ID_ATTRIBUTE];
53
+ }
45
54
  const CANONICAL_ARIA_ROLES = /* @__PURE__ */ new Set([
46
55
  "button",
47
56
  "link",
@@ -133,7 +142,16 @@ function xpathLiteralNode(value) {
133
142
  }
134
143
  return pieces.length === 1 ? pieces[0] : `concat(${pieces.join(", ")})`;
135
144
  }
136
- async function domEvaluationScript() {
145
+ async function domEvaluationScript(options) {
146
+ const { testIdAttributes } = options;
147
+ function resolveTestId(el) {
148
+ for (const attr of testIdAttributes) {
149
+ const value = el.getAttribute(attr);
150
+ if (value && value.trim())
151
+ return { attr, value };
152
+ }
153
+ return null;
154
+ }
137
155
  const DESTRUCTIVE_VERBS = /(delete|remove|destroy|submit|send|confirm|place\s*order|checkout|pay|purchase|buy|charge|cancel|close\s*account|deactivate|publish|approve|reject|logout|sign\s*out|save|create|update|add|edit|clear|reset|duplicate|clone|enable|disable|toggle|import|export|upload|download|invite|subscribe|unsubscribe|favorite|unfavorite|follow|unfollow|rename|archive|restore)/i;
138
156
  const VIEW_VERBS = /(view|show|open|see|preview|browse|navigate|go\s*to|back|next|previous|search|filter|sort|paginate|expand|collapse|select|choose|display|refresh|reload|close|dismiss|cancel\s*close|copy)/i;
139
157
  const SAFE_LANDMARK_DEFAULTS = /* @__PURE__ */ new Set(["navigation", "banner", "footer", "contentinfo"]);
@@ -165,9 +183,9 @@ async function domEvaluationScript() {
165
183
  return false;
166
184
  }
167
185
  }
168
- function testIdUnique(testId) {
186
+ function testIdUnique(attr, testId) {
169
187
  try {
170
- return document.querySelectorAll(`[data-testid=${CSS.escape(testId)}]`).length === 1;
188
+ return document.querySelectorAll(`[${attr}=${CSS.escape(testId)}]`).length === 1;
171
189
  } catch {
172
190
  return false;
173
191
  }
@@ -190,9 +208,11 @@ async function domEvaluationScript() {
190
208
  return pieces.length === 1 ? pieces[0] : `concat(${pieces.join(", ")})`;
191
209
  }
192
210
  function getXPath(el) {
193
- const testId = el.getAttribute("data-testid");
194
- if (testId && testIdUnique(testId))
195
- return `//*[@data-testid=${xpathLiteral(testId)}]`;
211
+ for (const attr of testIdAttributes) {
212
+ const value = el.getAttribute(attr);
213
+ if (value && value.trim() && testIdUnique(attr, value))
214
+ return `//*[@${attr}=${xpathLiteral(value)}]`;
215
+ }
196
216
  if (el.id && idUnique(el.id))
197
217
  return `//*[@id=${xpathLiteral(el.id)}]`;
198
218
  const parts = [];
@@ -236,30 +256,130 @@ async function domEvaluationScript() {
236
256
  "region",
237
257
  "form"
238
258
  ]);
239
- function getAccessibleName(el, role) {
240
- const ariaLabel = el.getAttribute("aria-label");
241
- if (ariaLabel && ariaLabel.trim())
242
- return ariaLabel.trim();
243
- const labelledBy = el.getAttribute("aria-labelledby");
244
- if (labelledBy) {
245
- const names = labelledBy.split(/\s+/).map((id) => document.getElementById(id)?.textContent?.trim()).filter(Boolean);
246
- if (names.length)
247
- return names.join(" ");
259
+ function isHiddenForName(el) {
260
+ if (el.getAttribute("aria-hidden") === "true")
261
+ return true;
262
+ const style = el.ownerDocument?.defaultView?.getComputedStyle(el) || null;
263
+ if (!style)
264
+ return false;
265
+ return style.display === "none" || style.visibility === "hidden";
266
+ }
267
+ function needsSeparator(el) {
268
+ if (el.tagName === "BR")
269
+ return true;
270
+ const style = el.ownerDocument?.defaultView?.getComputedStyle(el) || null;
271
+ if (!style)
272
+ return false;
273
+ const display = style.display;
274
+ return display !== "contents" && !display.startsWith("inline");
275
+ }
276
+ function embeddedControlValue(el) {
277
+ const tag = el.tagName;
278
+ if (tag === "TEXTAREA")
279
+ return el.value || null;
280
+ if (tag === "SELECT") {
281
+ const selected = Array.from(el.selectedOptions);
282
+ const text = selected.map((o) => o.textContent?.trim() ?? "").filter(Boolean).join(" ");
283
+ return text || null;
248
284
  }
285
+ if (tag !== "INPUT")
286
+ return null;
287
+ const input = el;
288
+ const type = (input.getAttribute("type") || "text").toLowerCase();
289
+ if (TEXTUAL_INPUT_TYPES.has(type) || type === "range")
290
+ return input.value || null;
291
+ return null;
292
+ }
293
+ const TEXTUAL_INPUT_TYPES = /* @__PURE__ */ new Set([
294
+ "text",
295
+ "search",
296
+ "email",
297
+ "url",
298
+ "tel",
299
+ "number"
300
+ ]);
301
+ function fallbackLabel(el) {
302
+ const alt = el.getAttribute("alt")?.trim() || null;
303
+ const tag = el.tagName;
304
+ const nativeAlt = tag === "IMG" || tag === "AREA" || tag === "INPUT" && (el.getAttribute("type") || "").toLowerCase() === "image";
305
+ if (nativeAlt && alt)
306
+ return alt;
249
307
  const title = el.getAttribute("title");
250
308
  if (title && title.trim())
251
309
  return title.trim();
252
310
  const placeholder = el.getAttribute("placeholder");
253
311
  if (placeholder && placeholder.trim())
254
312
  return placeholder.trim();
255
- const alt = el.getAttribute("alt");
256
- if (alt && alt.trim())
257
- return alt.trim();
258
- if (role && NAME_FROM_CONTENT_PROHIBITED.has(role))
313
+ return alt;
314
+ }
315
+ function labelledByName(el, depth) {
316
+ const labelledBy = el.getAttribute("aria-labelledby");
317
+ if (!labelledBy || depth > MAX_NAME_DEPTH)
318
+ return null;
319
+ const names = [];
320
+ for (const id of labelledBy.split(/\s+/)) {
321
+ const ref = id ? document.getElementById(id) : null;
322
+ if (!ref)
323
+ continue;
324
+ const ownLabel = ref.getAttribute("aria-label")?.trim();
325
+ const name = ownLabel || collapse(contentName(ref, depth + 1, true));
326
+ if (name)
327
+ names.push(name);
328
+ }
329
+ return names.length ? names.join(" ") : null;
330
+ }
331
+ const MAX_NAME_DEPTH = 20;
332
+ function collapse(text) {
333
+ return text.trim().replace(/\s+/g, " ");
334
+ }
335
+ function contentName(el, depth = 0, includeHidden = false) {
336
+ if (depth > MAX_NAME_DEPTH)
259
337
  return "";
260
- const text = el.textContent?.trim().replace(/\s+/g, " ").slice(0, 60);
338
+ let out = "";
339
+ for (const node of Array.from(el.childNodes)) {
340
+ if (node.nodeType === Node.TEXT_NODE) {
341
+ out += node.nodeValue ?? "";
342
+ continue;
343
+ }
344
+ if (node.nodeType !== Node.ELEMENT_NODE)
345
+ continue;
346
+ const child = node;
347
+ if (!includeHidden && isHiddenForName(child))
348
+ continue;
349
+ const separator = needsSeparator(child) ? " " : "";
350
+ let contribution = embeddedControlValue(child);
351
+ if (contribution === null) {
352
+ const childLabelledBy = labelledByName(child, depth + 1);
353
+ const childAriaLabel = child.getAttribute("aria-label")?.trim();
354
+ if (childLabelledBy)
355
+ contribution = childLabelledBy;
356
+ else if (childAriaLabel)
357
+ contribution = childAriaLabel;
358
+ }
359
+ if (contribution !== null) {
360
+ out += `${separator} ${contribution} ${separator}`;
361
+ continue;
362
+ }
363
+ const inner = contentName(child, depth + 1, includeHidden);
364
+ out += inner.trim() ? `${separator}${inner}${separator}` : `${separator} ${fallbackLabel(child) ?? ""} ${separator}`;
365
+ }
366
+ return out;
367
+ }
368
+ function getAccessibleName(el, role) {
369
+ const labelledBy = labelledByName(el, 0);
370
+ if (labelledBy)
371
+ return labelledBy;
372
+ const ariaLabel = el.getAttribute("aria-label");
373
+ if (ariaLabel && ariaLabel.trim())
374
+ return ariaLabel.trim();
375
+ if (role && NAME_FROM_CONTENT_PROHIBITED.has(role))
376
+ return fallbackLabel(el) ?? "";
377
+ const text = collapse(contentName(el)).slice(0, 60);
261
378
  if (text)
262
379
  return text;
380
+ const fallback = fallbackLabel(el);
381
+ if (fallback)
382
+ return fallback;
263
383
  if (NAMELESS_INTERACTIVE_ICON_ROLES.has(role)) {
264
384
  const iconName = findDescendantIconName(el);
265
385
  if (iconName)
@@ -503,8 +623,8 @@ async function domEvaluationScript() {
503
623
  let depth = 0;
504
624
  while (cur && depth < ANCESTOR_ID_MAX_DEPTH) {
505
625
  if (isElementVisible(cur)) {
506
- const testId = cur.getAttribute("data-testid");
507
- if (testId) return testId;
626
+ const ancestorTestId = resolveTestId(cur);
627
+ if (ancestorTestId) return ancestorTestId.value;
508
628
  const id = cur.getAttribute("id");
509
629
  if (id && !isDynamicIdClient(id)) return id;
510
630
  }
@@ -722,7 +842,7 @@ async function domEvaluationScript() {
722
842
  if (!accessibleName && !LIVE_REGION_ROLES.has(role))
723
843
  return null;
724
844
  const idAttr = el.getAttribute("id");
725
- const testIdAttr = el.getAttribute("data-testid");
845
+ const resolvedTestId = resolveTestId(el);
726
846
  const fingerprint = widgetType === "native" ? null : await computeElementFingerprint(el);
727
847
  const stableIdValue = idAttr && idUnique(idAttr) && !isDynamicIdClient(idAttr) ? idAttr : null;
728
848
  return {
@@ -734,7 +854,8 @@ async function domEvaluationScript() {
734
854
  framePath: computeFramePath(el),
735
855
  shadowRoot: hasShadowRootAncestor(el),
736
856
  stableId: stableIdValue,
737
- testId: testIdAttr || null,
857
+ testId: resolvedTestId?.value ?? null,
858
+ testIdAttr: resolvedTestId?.attr ?? null,
738
859
  fingerprint,
739
860
  visible: isElementVisible(el),
740
861
  nearestAncestorId: getNearestAncestorId(el),
@@ -757,10 +878,11 @@ async function domEvaluationScript() {
757
878
  const pageHash = `${elements.length}:${hashAccum}`;
758
879
  return { elements, pageHash };
759
880
  }
760
- async function buildPageBlueprint(page) {
881
+ async function buildPageBlueprint(page, testIdAttribute) {
882
+ const testIdAttributes = resolveTestIdAttributes(testIdAttribute);
761
883
  const [pageUrl, evalResult] = await Promise.all([
762
884
  page.url(),
763
- page.evaluate(domEvaluationScript)
885
+ page.evaluate(domEvaluationScript, { testIdAttributes })
764
886
  ]);
765
887
  const unknownRoles = /* @__PURE__ */ new Set();
766
888
  for (const el of evalResult.elements) {
@@ -977,6 +1099,9 @@ function analyseByVariableName(group, xpathTemplate) {
977
1099
  function analyseByVaryingTestId(group) {
978
1100
  if (group.some((el) => !el.testId))
979
1101
  return null;
1102
+ const testIdAttribute = group[0].testIdAttr;
1103
+ if (!testIdAttribute || group.some((el) => el.testIdAttr !== testIdAttribute))
1104
+ return null;
980
1105
  const firstName = group[0].accessibleName;
981
1106
  if (!group.every((el) => el.accessibleName === firstName))
982
1107
  return null;
@@ -995,7 +1120,7 @@ function analyseByVaryingTestId(group) {
995
1120
  const firstToken = firstName.toLowerCase().split(/\s+/)[0];
996
1121
  const slugParamName = guessParamName(firstToken, "") || "itemSlug";
997
1122
  const testIdPattern = `${commonPrefix}{${slugParamName}}${commonSuffix}`;
998
- const xpathBySlug = `//*[@data-testid=${xpathLiteralNode(testIdPattern)}]`;
1123
+ const xpathBySlug = `//*[@${testIdAttribute}=${xpathLiteralNode(testIdPattern)}]`;
999
1124
  const parameters = [slugParamName];
1000
1125
  const indexParameter = slugParamName;
1001
1126
  const accessibleNameTemplate = firstName;
@@ -1214,6 +1339,7 @@ function buildOutline(blueprint) {
1214
1339
  // Annotate the CommonJS export names for ESM import in node:
1215
1340
  0 && (module.exports = {
1216
1341
  BlueprintInvariantError,
1342
+ DEFAULT_TEST_ID_ATTRIBUTE,
1217
1343
  applyPositionHintToNamelessElements,
1218
1344
  buildMap,
1219
1345
  buildOutline,
@@ -1221,6 +1347,7 @@ function buildOutline(blueprint) {
1221
1347
  deriveLogicalName,
1222
1348
  resolveAccessibleName,
1223
1349
  resolveTemplate,
1350
+ resolveTestIdAttributes,
1224
1351
  resolveXpath,
1225
1352
  validateElement,
1226
1353
  validateRepeatingElement,
@@ -214,7 +214,7 @@ async function crawl(entryUrl, options) {
214
214
  }
215
215
  let blueprint;
216
216
  try {
217
- blueprint = await (0, import_blueprint.buildPageBlueprint)(page);
217
+ blueprint = await (0, import_blueprint.buildPageBlueprint)(page, options.testIdAttribute);
218
218
  } catch (err) {
219
219
  if (err instanceof import_blueprint.BlueprintInvariantError) {
220
220
  console.warn(`[dom-analyzer] Skipping blueprint for ${page.url()} \u2014 invariant violated: ${err.message}`);
@@ -327,7 +327,7 @@ async function crawlSinglePage(entryUrl, options) {
327
327
  await page.goto(entryUrl, { waitUntil: "networkidle" });
328
328
  let blueprint;
329
329
  try {
330
- blueprint = await (0, import_blueprint.buildPageBlueprint)(page);
330
+ blueprint = await (0, import_blueprint.buildPageBlueprint)(page, options.testIdAttribute);
331
331
  } catch (err) {
332
332
  if (err instanceof import_blueprint.BlueprintInvariantError) {
333
333
  console.warn(`[dom-analyzer] Blueprint skipped \u2014 invariant violated: ${err.message}`);
@@ -67,7 +67,7 @@ const blueprint = (0, import_tool.defineTabTool)({
67
67
  },
68
68
  handle: async (tab, _params, response) => {
69
69
  try {
70
- const current = await (0, import_blueprint.buildPageBlueprint)(tab.page);
70
+ const current = await (0, import_blueprint.buildPageBlueprint)(tab.page, tab.context.config.testIdAttribute);
71
71
  const url = current.url;
72
72
  const previous = tab.blueprintCache.get(url);
73
73
  if (!previous) {
@@ -119,10 +119,14 @@ Use browser_sitemap_query to read any page. Pass refresh=true to recrawl.`
119
119
  maxPages,
120
120
  sameOriginOnly,
121
121
  probeButtons: probeMode,
122
- playwrightStoragePath
122
+ playwrightStoragePath,
123
+ testIdAttribute: context.config.testIdAttribute
123
124
  });
124
125
  } else {
125
- sitemap = await (0, import_crawler.crawlSinglePage)(url, { playwrightStoragePath });
126
+ sitemap = await (0, import_crawler.crawlSinglePage)(url, {
127
+ playwrightStoragePath,
128
+ testIdAttribute: context.config.testIdAttribute
129
+ });
126
130
  }
127
131
  context.sitemapCache.set(key, { sitemap, storageStateHash, probeButtonsMode: probeMode });
128
132
  const pageCount = Object.keys(sitemap.pages).length;
@@ -36,7 +36,8 @@ const loadTraceSchema = {
36
36
  name: "skyramp_load_trace",
37
37
  title: "Load and replay a Skyramp trace",
38
38
  description: [
39
- "Load a previously recorded Skyramp trace (.zip or .jsonl/.txt) and replay a range of its actions against the live browser to restore state, then continue recording from that point.",
39
+ "Load a previously recorded Skyramp trace (.zip or .jsonl/.txt) OR a generated Skyramp test file (.spec.ts/.ts/.js/.py) and replay a range of its actions against the live browser to restore state, then continue recording from that point.",
40
+ "A test file is a lossy projection of its original trace: the replayable interaction skeleton (navigate/click/fill/press/...) is extracted, while codegen enrichments with no replay primitive (waitForResponse, payload assertions) are skipped and reported. Step numbers refer to the converted steps \u2014 use dryRun to see them.",
40
41
  "The replayed actions are merged into the current recording, so subsequent browser_* steps append to them and skyramp_export_zip writes a combined trace. You can record some steps FIRST, then load a trace to insert an already-recorded fragment at the current point.",
41
42
  "STARTING LATER (skip a stale prefix): set startAtStep to begin replay partway in, skipping earlier steps. Use this when the live browser is already past the trace's opening steps - e.g. the login page changed, you recorded the NEW login manually, and now want to append only the still-valid post-login part of the old trace. Omit to start from step 1.",
42
43
  "- startAtStep: begin replay at step N (1-based, from the numbered step list); steps 1..N-1 are skipped (neither replayed nor recorded).",
@@ -47,7 +48,7 @@ const loadTraceSchema = {
47
48
  "If unsure where to start/stop, first call with dryRun:true to read the numbered step list, then call again with the chosen start/stop parameters."
48
49
  ].join(" "),
49
50
  inputSchema: import_mcpBundle.z.object({
50
- path: import_mcpBundle.z.string().describe("Absolute path to the trace file (.zip, .jsonl, or .txt)."),
51
+ path: import_mcpBundle.z.string().describe("Absolute path to the trace file (.zip, .jsonl, or .txt) or a generated Skyramp test file (.spec.ts, .ts, .js, .py)."),
51
52
  startAtStep: import_mcpBundle.z.number().int().positive().optional().describe("Begin replay at step N (1-based); skip steps 1..N-1. Use to skip a stale prefix (e.g. old login)."),
52
53
  stopAtStep: import_mcpBundle.z.number().int().positive().optional().describe("Replay up to and including step N (1-based), then stop before continuing."),
53
54
  stopAtUrl: import_mcpBundle.z.string().optional().describe("Stop once the active page URL matches this glob/regex/substring."),
@@ -24,9 +24,10 @@ module.exports = __toCommonJS(resultCode_exports);
24
24
  function rewriteResultCodeText(text, newCode) {
25
25
  if (!text.includes("await page."))
26
26
  return null;
27
- const fence = text.match(/```(?:js|javascript)\n/);
28
- if (fence && fence.index !== void 0) {
29
- const codeStart = fence.index + fence[0].length;
27
+ const JS_FENCE = "```js\n";
28
+ const fenceIdx = text.indexOf(JS_FENCE);
29
+ if (fenceIdx >= 0) {
30
+ const codeStart = fenceIdx + JS_FENCE.length;
30
31
  const codeEnd = text.indexOf("\n```", codeStart);
31
32
  if (codeEnd >= 0)
32
33
  return text.slice(0, codeStart) + newCode + text.slice(codeEnd);
@@ -31,11 +31,14 @@ __export(skyRampImport_exports, {
31
31
  actionsFromFile: () => actionsFromFile,
32
32
  actionsFromPath: () => actionsFromPath,
33
33
  actionsFromZip: () => actionsFromZip,
34
- parseJsonl: () => parseJsonl
34
+ parseJsonl: () => parseJsonl,
35
+ traceSourceFromPath: () => traceSourceFromPath
35
36
  });
36
37
  module.exports = __toCommonJS(skyRampImport_exports);
37
38
  var import_fs = __toESM(require("fs"));
39
+ var import_path = __toESM(require("path"));
38
40
  var import_utils = require("playwright-core/lib/utils");
41
+ var import_specImport = require("./specImport");
39
42
  const SKYRAMP_ACTIVITIES_FILE = "skyramp_playwright.txt";
40
43
  const ACTION_NAMES = /* @__PURE__ */ new Set([
41
44
  "marker",
@@ -101,8 +104,16 @@ async function actionsFromZip(zipPath) {
101
104
  function actionsFromFile(jsonlPath) {
102
105
  return parseJsonl(import_fs.default.readFileSync(jsonlPath, "utf-8"));
103
106
  }
107
+ async function traceSourceFromPath(tracePath) {
108
+ if (tracePath.endsWith(".zip"))
109
+ return { actions: await actionsFromZip(tracePath), diagnostics: [] };
110
+ const specLanguage = (0, import_specImport.specLanguageForPath)(tracePath);
111
+ if (specLanguage)
112
+ return (0, import_specImport.specToActions)(import_fs.default.readFileSync(tracePath, "utf-8"), specLanguage, import_path.default.dirname(tracePath));
113
+ return { actions: actionsFromFile(tracePath), diagnostics: [] };
114
+ }
104
115
  async function actionsFromPath(tracePath) {
105
- return tracePath.endsWith(".zip") ? await actionsFromZip(tracePath) : actionsFromFile(tracePath);
116
+ return (await traceSourceFromPath(tracePath)).actions;
106
117
  }
107
118
  function parseJsonl(content) {
108
119
  const result = [];
@@ -143,5 +154,6 @@ function parseLine(line) {
143
154
  actionsFromFile,
144
155
  actionsFromPath,
145
156
  actionsFromZip,
146
- parseJsonl
157
+ parseJsonl,
158
+ traceSourceFromPath
147
159
  });