@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.
- package/build/index.js +47 -2
- package/build/playwright/PlaywrightTraceService.d.ts +8 -0
- package/build/playwright/PlaywrightTraceService.js +1 -0
- package/build/playwright/registerPlaywrightTools.js +42 -1
- package/build/prompts/enhance-assertions/sharedAssertionRules.js +19 -0
- package/build/prompts/pom-aware-code-reuse.js +17 -8
- package/build/prompts/test-maintenance/actionsInstructions.js +2 -2
- package/build/prompts/test-recommendation/analysisOutputPrompt.js +1 -4
- package/build/prompts/test-recommendation/recommendationSections.d.ts +1 -1
- package/build/prompts/test-recommendation/recommendationSections.js +5 -5
- package/build/prompts/test-recommendation/test-recommendation-prompt.js +13 -7
- package/build/prompts/testbot/testbot-prompts.d.ts +2 -12
- package/build/prompts/testbot/testbot-prompts.js +28 -16
- package/build/recommendation/discriminators.d.ts +7 -1
- package/build/recommendation/discriminators.js +16 -3
- package/build/resources/testbotResource.js +20 -4
- package/build/services/ScenarioGenerationService.js +5 -2
- package/build/services/TestExecutionService.d.ts +13 -8
- package/build/services/TestExecutionService.js +73 -26
- package/build/services/TestGenerationService.js +24 -9
- package/build/services/containerEnv.d.ts +12 -1
- package/build/services/containerEnv.js +118 -1
- package/build/tools/executeSkyrampTestTool.d.ts +9 -0
- package/build/tools/executeSkyrampTestTool.js +20 -6
- package/build/tools/execution-video-state.d.ts +21 -0
- package/build/tools/execution-video-state.js +51 -0
- package/build/tools/generate-tests/generateBatchScenarioRestTool.js +31 -11
- package/build/tools/generate-tests/planGuard.d.ts +5 -5
- package/build/tools/generate-tests/planGuard.js +5 -17
- package/build/tools/queryProxyMocksTool.js +0 -1
- package/build/tools/submitReportTool.d.ts +83 -10
- package/build/tools/submitReportTool.js +179 -29
- package/build/tools/test-management/actionsTool.js +52 -41
- package/build/tools/test-management/analyzeChangesTool.d.ts +11 -0
- package/build/tools/test-management/analyzeChangesTool.js +37 -33
- package/build/tools/test-management/analyzeTestHealthTool.js +3 -3
- package/build/tools/test-management/registerTestPlanTool.js +113 -31
- package/build/types/TestAnalysis.d.ts +7 -3
- package/build/types/TestExecution.d.ts +14 -0
- package/build/types/TestTypes.js +3 -2
- package/build/types/TestbotPromptOptions.d.ts +34 -0
- package/build/types/TestbotPromptOptions.js +1 -0
- package/build/types/TestbotReport.d.ts +10 -0
- package/build/types/TestbotReport.js +10 -1
- package/build/types/index.d.ts +2 -0
- package/build/types/index.js +1 -0
- package/build/utils/AnalysisStateManager.d.ts +36 -2
- package/build/utils/AnalysisStateManager.js +34 -13
- package/build/utils/frontendSelectors.js +0 -1
- package/build/utils/gitStaging.d.ts +5 -0
- package/build/utils/gitStaging.js +1 -1
- package/build/utils/pom-catalog.d.ts +23 -0
- package/build/utils/pom-catalog.js +30 -0
- package/build/utils/pom-scope/pom-files.d.ts +14 -0
- package/build/utils/pom-scope/pom-files.js +32 -6
- package/build/utils/pom-scope/testIdDiscovery.d.ts +40 -0
- package/build/utils/pom-scope/testIdDiscovery.js +104 -0
- package/build/utils/reportVerification.d.ts +7 -2
- package/build/utils/reportVerification.js +9 -3
- package/build/utils/scenarioDrafting.js +7 -1
- package/build/utils/skyrampMdContent.d.ts +1 -1
- package/build/utils/skyrampMdContent.js +1 -1
- package/build/utils/urlPath.d.ts +37 -0
- package/build/utils/urlPath.js +55 -0
- package/build/utils/utils.d.ts +45 -0
- package/build/utils/utils.js +50 -0
- package/build/utils/versions.d.ts +3 -3
- package/build/utils/versions.js +1 -1
- package/build/utils/workspaceAuth.d.ts +15 -15
- package/build/utils/workspaceAuth.js +32 -17
- package/build/workspace/queryParamResolution.d.ts +93 -0
- package/build/workspace/queryParamResolution.js +201 -0
- package/build/workspace/workspace.d.ts +104 -0
- package/build/workspace/workspace.js +24 -0
- package/node_modules/playwright/ThirdPartyNotices.txt +319 -266
- package/node_modules/playwright/lib/dom-analyzer/blueprint.js +154 -27
- package/node_modules/playwright/lib/dom-analyzer/crawler.js +2 -2
- package/node_modules/playwright/lib/mcp/browser/tools/pageBlueprint.js +1 -1
- package/node_modules/playwright/lib/mcp/browser/tools/sitemap.js +6 -2
- package/node_modules/playwright/lib/mcp/skyramp/loadTraceTool.js +3 -2
- package/node_modules/playwright/lib/mcp/skyramp/resultCode.js +4 -3
- package/node_modules/playwright/lib/mcp/skyramp/skyRampImport.js +15 -3
- package/node_modules/playwright/lib/mcp/skyramp/specImport.js +781 -0
- package/node_modules/playwright/lib/mcp/skyramp/traceRecordingBackend.js +14 -3
- package/node_modules/playwright/lib/mcp/test/resultCode.test.js +2 -1
- package/node_modules/playwright/lib/mcp/test/skyRampExport.js +1 -1
- package/node_modules/playwright/lib/mcp/test/skyRampExport.test.js +30 -0
- package/node_modules/playwright/lib/transform/babelBundleImpl.js +200 -199
- package/node_modules/playwright/node_modules/playwright-core/ThirdPartyNotices.txt +3 -3
- package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/.package-lock.json +3 -3
- package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/adapter/aws-lambda/handler.js +16 -25
- package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/adapter/bun/websocket.js +3 -1
- package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/adapter/lambda-edge/handler.js +20 -4
- package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/cjs/adapter/aws-lambda/handler.js +16 -25
- package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/cjs/adapter/bun/websocket.js +3 -1
- package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/cjs/adapter/lambda-edge/handler.js +20 -4
- package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/cjs/client/client.js +10 -1
- package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/cjs/client/utils.js +1 -1
- package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/cjs/helper/css/common.js +3 -1
- package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/cjs/helper/css/index.js +9 -1
- package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/cjs/jsx/base.js +8 -14
- package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/cjs/jsx/components.js +41 -21
- package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/cjs/jsx/context.js +131 -5
- package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/cjs/jsx/streaming.js +9 -7
- package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/cjs/middleware/bearer-auth/index.js +1 -1
- package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/cjs/middleware/cache/index.js +1 -1
- package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/cjs/middleware/compress/index.js +2 -1
- package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/cjs/middleware/cors/index.js +2 -5
- package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/cjs/middleware/etag/index.js +2 -1
- package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/cjs/middleware/language/language.js +10 -32
- package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/cjs/middleware/method-override/index.js +5 -3
- package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/cjs/middleware/serve-static/index.js +2 -2
- package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/cjs/middleware/timing/timing.js +3 -1
- package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/cjs/router/trie-router/node.js +9 -0
- package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/cjs/utils/body.js +12 -4
- package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/cjs/utils/buffer.js +2 -1
- package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/cjs/utils/ipaddr.js +6 -1
- package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/cjs/validator/validator.js +3 -3
- package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/client/client.js +10 -1
- package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/client/utils.js +1 -1
- package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/helper/css/common.js +3 -1
- package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/helper/css/index.js +9 -1
- package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/jsx/base.js +15 -15
- package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/jsx/components.js +42 -22
- package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/jsx/context.js +129 -5
- package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/jsx/streaming.js +10 -8
- package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/middleware/bearer-auth/index.js +1 -1
- package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/middleware/cache/index.js +1 -1
- package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/middleware/compress/index.js +2 -1
- package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/middleware/cors/index.js +2 -5
- package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/middleware/etag/index.js +2 -1
- package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/middleware/language/language.js +10 -32
- package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/middleware/method-override/index.js +5 -3
- package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/middleware/serve-static/index.js +2 -2
- package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/middleware/timing/timing.js +3 -1
- package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/router/trie-router/node.js +9 -0
- package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/types/adapter/aws-lambda/handler.d.ts +1 -1
- package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/types/adapter/lambda-edge/handler.d.ts +1 -1
- package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/types/helper/websocket/index.d.ts +1 -1
- package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/types/jsx/base.d.ts +1 -3
- package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/types/jsx/context.d.ts +39 -0
- package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/types/middleware/context-storage/index.d.ts +2 -2
- package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/types/middleware/language/language.d.ts +18 -0
- package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/types/utils/body.d.ts +1 -1
- package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/types/utils/types.d.ts +1 -1
- package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/utils/body.js +12 -4
- package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/utils/buffer.js +2 -1
- package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/utils/ipaddr.js +6 -1
- package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/validator/validator.js +3 -3
- package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/package.json +29 -22
- package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/package-lock.json +3 -3
- package/node_modules/playwright/node_modules/playwright-core/lib/generated/pollingRecorderSource.js +1 -1
- package/node_modules/playwright/node_modules/playwright-core/lib/server/codegen/skyramp/jsonlReader.js +5 -1
- package/node_modules/playwright/node_modules/playwright-core/lib/server/codegen/skyramp/specReader.js +781 -0
- package/node_modules/playwright/node_modules/playwright-core/lib/server/recorder/recorderApp.js +25 -6
- package/node_modules/playwright/node_modules/playwright-core/lib/server/recorder/skyramp/replayEngine.js +3 -1
- package/node_modules/playwright/node_modules/playwright-core/lib/utils.js +2 -0
- package/node_modules/playwright/node_modules/playwright-core/lib/vite/htmlReport/index.html +253 -27
- package/node_modules/playwright/node_modules/playwright-core/lib/vite/recorder/assets/{codeMirrorModule-D0BjbCb7.js → codeMirrorModule-DtudTj_v.js} +1 -1
- package/node_modules/playwright/node_modules/playwright-core/lib/vite/recorder/assets/index-BpDwp16L.js +422 -0
- package/node_modules/playwright/node_modules/playwright-core/lib/vite/recorder/index.html +1 -1
- package/node_modules/playwright/node_modules/playwright-core/lib/vite/traceViewer/assets/{codeMirrorModule-Bzd72-bG.js → codeMirrorModule-FNMuBzX1.js} +1 -1
- package/node_modules/playwright/node_modules/playwright-core/lib/vite/traceViewer/assets/defaultSettingsView-Co9upU5h.js +1035 -0
- package/node_modules/playwright/node_modules/playwright-core/lib/vite/traceViewer/index.DXNIQ_dx.js +2 -0
- 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.CIKB3XSv.js +5 -0
- package/node_modules/playwright/node_modules/playwright-core/lib/vite/traceViewer/uiMode.html +2 -2
- package/node_modules/playwright/node_modules/playwright-core/package.json +1 -1
- package/node_modules/playwright/node_modules/playwright-core/src/generated/pollingRecorderSource.ts +1 -1
- package/node_modules/playwright/node_modules/playwright-core/src/server/codegen/skyramp/jsonlReader.ts +4 -0
- package/node_modules/playwright/node_modules/playwright-core/src/server/codegen/skyramp/specReader.ts +1028 -0
- package/node_modules/playwright/node_modules/playwright-core/src/server/recorder/recorderApp.ts +31 -8
- package/node_modules/playwright/node_modules/playwright-core/src/server/recorder/skyramp/replayEngine.ts +1 -0
- package/node_modules/playwright/node_modules/playwright-core/src/utils.ts +1 -0
- package/node_modules/playwright/package.json +1 -1
- package/package.json +10 -6
- package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/tsconfig.build.tsbuildinfo +0 -1
- package/node_modules/playwright/node_modules/playwright-core/lib/vite/recorder/assets/index-lvTRGFx-.js +0 -193
- package/node_modules/playwright/node_modules/playwright-core/lib/vite/traceViewer/assets/defaultSettingsView-DzxTioTK.js +0 -809
- package/node_modules/playwright/node_modules/playwright-core/lib/vite/traceViewer/index.BGc30U3S.js +0 -2
- 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(`[
|
|
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
|
|
194
|
-
|
|
195
|
-
|
|
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
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
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
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
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
|
-
|
|
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
|
|
507
|
-
if (
|
|
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
|
|
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:
|
|
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 = `//*[
|
|
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, {
|
|
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
|
|
28
|
-
|
|
29
|
-
|
|
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
|
|
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
|
});
|