@skyramp/mcp 0.3.4 → 0.3.6-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.
- package/build/playwright/registerPlaywrightTools.js +92 -30
- package/build/playwright/traceRecordingPrompt.d.ts +6 -0
- package/build/playwright/traceRecordingPrompt.js +6 -2
- package/build/prompts/code-reuse.d.ts +1 -2
- package/build/prompts/code-reuse.js +182 -77
- package/build/prompts/modularization/integration-test-modularization.d.ts +2 -0
- package/build/prompts/modularization/integration-test-modularization.js +83 -41
- package/build/prompts/modularization/render.d.ts +18 -0
- package/build/prompts/modularization/render.js +12 -0
- package/build/prompts/modularization/ui-test-modularization.d.ts +3 -1
- package/build/prompts/modularization/ui-test-modularization.js +89 -47
- package/build/prompts/pom-aware-code-reuse.js +3 -1
- package/build/prompts/shared-helper-policy.d.ts +57 -0
- package/build/prompts/shared-helper-policy.js +135 -0
- package/build/prompts/test-recommendation/diffExecutionPlan.js +62 -56
- package/build/prompts/test-recommendation/fullRepoCatalog.js +19 -8
- package/build/prompts/test-recommendation/recommendationShared.d.ts +28 -6
- package/build/prompts/test-recommendation/recommendationShared.js +90 -16
- package/build/prompts/test-recommendation/registerRecommendTestsPrompt.js +22 -0
- package/build/prompts/test-recommendation/test-recommendation-prompt.d.ts +2 -2
- package/build/prompts/test-recommendation/test-recommendation-prompt.js +3 -3
- package/build/prompts/testbot/testbot-prompts.js +88 -33
- package/build/recommendation/budgeters/shared.js +105 -27
- package/build/recommendation/discriminators.js +13 -2
- package/build/recommendation/planRanker.d.ts +6 -6
- package/build/recommendation/planRanker.js +6 -61
- package/build/services/AnalyticsService.d.ts +7 -0
- package/build/services/AnalyticsService.js +7 -1
- package/build/services/ModularizationService.js +1 -3
- package/build/services/TestDiscoveryService.d.ts +0 -2
- package/build/services/TestDiscoveryService.js +2 -37
- package/build/services/TestGenerationService.d.ts +16 -0
- package/build/services/TestGenerationService.js +86 -10
- package/build/services/containerEnv.js +13 -12
- package/build/tools/code-refactor/codeReuseTool.js +279 -93
- package/build/tools/code-refactor/enhance-state.d.ts +49 -0
- package/build/tools/code-refactor/enhance-state.js +109 -0
- package/build/tools/code-refactor/enhanceAssertionsTool.js +34 -1
- package/build/tools/code-refactor/modularizationTool.js +9 -2
- package/build/tools/code-refactor/reuse-outcome.d.ts +23 -1
- package/build/tools/code-refactor/reuse-outcome.js +14 -4
- package/build/tools/code-refactor/reuse-state.d.ts +127 -5
- package/build/tools/code-refactor/reuse-state.js +628 -16
- package/build/tools/code-refactor/utils-verify-gates.d.ts +26 -0
- package/build/tools/code-refactor/utils-verify-gates.js +100 -0
- package/build/tools/code-refactor/verify-gates.d.ts +2 -1
- package/build/tools/code-refactor/verify-gates.js +90 -25
- package/build/tools/executeSkyrampTestTool.d.ts +19 -0
- package/build/tools/executeSkyrampTestTool.js +158 -8
- package/build/tools/generate-tests/generateBatchScenarioRestTool.js +2 -2
- package/build/tools/generate-tests/generateE2ERestTool.js +16 -0
- package/build/tools/generate-tests/generateUIRestTool.d.ts +1 -0
- package/build/tools/generate-tests/generateUIRestTool.js +22 -0
- package/build/tools/generate-tests/scenarioLint.d.ts +2 -0
- package/build/tools/generate-tests/scenarioLint.js +127 -19
- package/build/tools/generate-tests/trace-reuse-guard.d.ts +20 -0
- package/build/tools/generate-tests/trace-reuse-guard.js +93 -0
- package/build/tools/submitReportTool.d.ts +38 -38
- package/build/tools/submitReportTool.js +487 -104
- package/build/tools/test-management/analyzeChangesTool.d.ts +24 -1
- package/build/tools/test-management/analyzeChangesTool.js +75 -12
- package/build/tools/test-management/analyzeTestHealthTool.js +7 -7
- package/build/tools/test-management/registerTestPlanTool.d.ts +203 -0
- package/build/tools/test-management/registerTestPlanTool.js +149 -23
- package/build/types/Recommendation.d.ts +34 -5
- package/build/types/RepositoryAnalysis.d.ts +133 -114
- package/build/types/RepositoryAnalysis.js +1 -1
- package/build/types/ReuseOutcome.d.ts +102 -6
- package/build/types/ReuseOutcome.js +16 -2
- package/build/types/TestRecommendation.js +21 -3
- package/build/types/TestTypes.js +14 -8
- package/build/types/TestbotReport.d.ts +25 -3
- package/build/types/index.d.ts +2 -2
- package/build/types/index.js +1 -1
- package/build/utils/AnalysisStateManager.d.ts +69 -1
- package/build/utils/AnalysisStateManager.js +69 -5
- package/build/utils/branchDiff.d.ts +10 -0
- package/build/utils/branchDiff.js +28 -0
- package/build/utils/changedRoutes.d.ts +29 -0
- package/build/utils/changedRoutes.js +87 -0
- package/build/utils/featureFlags.d.ts +21 -0
- package/build/utils/featureFlags.js +23 -0
- package/build/utils/frontendIntegration.js +34 -4
- package/build/utils/importerHop.d.ts +2 -8
- package/build/utils/importerHop.js +15 -53
- package/build/utils/pathMatching.d.ts +38 -0
- package/build/utils/pathMatching.js +71 -0
- package/build/utils/pathSignatures.d.ts +22 -0
- package/build/utils/pathSignatures.js +57 -0
- package/build/utils/planMatchKeys.d.ts +16 -3
- package/build/utils/planMatchKeys.js +26 -10
- package/build/utils/pluralization.d.ts +10 -0
- package/build/utils/pluralization.js +18 -0
- package/build/utils/pom-catalog-parse.d.ts +52 -0
- package/build/utils/pom-catalog-parse.js +141 -0
- package/build/utils/pom-scope/selector-extractor.d.ts +12 -0
- package/build/utils/pom-scope/selector-extractor.js +34 -8
- package/build/utils/pom-verify/verify.d.ts +6 -5
- package/build/utils/pom-verify/verify.js +8 -6
- package/build/utils/reportLanguage.d.ts +43 -0
- package/build/utils/reportLanguage.js +125 -0
- package/build/utils/reportVerification.d.ts +74 -4
- package/build/utils/reportVerification.js +259 -3
- package/build/utils/reuseRouting.d.ts +3 -0
- package/build/utils/reuseRouting.js +50 -0
- package/build/utils/routeParsers.d.ts +2 -0
- package/build/utils/routeParsers.js +65 -8
- package/build/utils/scenarioDrafting.d.ts +1 -1
- package/build/utils/scenarioDrafting.js +57 -45
- package/build/utils/subjectEndpoints.d.ts +19 -0
- package/build/utils/subjectEndpoints.js +98 -0
- package/build/utils/testFileClassification.d.ts +11 -0
- package/build/utils/testFileClassification.js +47 -0
- package/build/utils/uiPageEnumerator.d.ts +45 -19
- package/build/utils/uiPageEnumerator.js +95 -51
- package/build/utils/utils-verify/allow.d.ts +16 -0
- package/build/utils/utils-verify/allow.js +68 -0
- package/build/utils/utils-verify/call-sites.d.ts +34 -0
- package/build/utils/utils-verify/call-sites.js +154 -0
- package/build/utils/utils-verify/index.d.ts +7 -0
- package/build/utils/utils-verify/index.js +7 -0
- package/build/utils/utils-verify/language-spec.d.ts +91 -0
- package/build/utils/utils-verify/language-spec.js +210 -0
- package/build/utils/utils-verify/locate.d.ts +39 -0
- package/build/utils/utils-verify/locate.js +199 -0
- package/build/utils/utils-verify/parse.d.ts +34 -0
- package/build/utils/utils-verify/parse.js +177 -0
- package/build/utils/utils-verify/stage.d.ts +24 -0
- package/build/utils/utils-verify/stage.js +107 -0
- package/build/utils/utils-verify/verify.d.ts +63 -0
- package/build/utils/utils-verify/verify.js +168 -0
- package/build/utils/utils.d.ts +3 -1
- package/build/utils/utils.js +3 -1
- package/build/workspace/workspace.d.ts +32 -32
- package/node_modules/playwright/lib/mcp/skyramp/assertTool.js +9 -5
- package/node_modules/playwright/lib/mcp/skyramp/loadTraceTool.js +16 -0
- package/node_modules/playwright/lib/mcp/skyramp/skyRampImport.js +2 -0
- package/node_modules/playwright/lib/mcp/skyramp/traceRecordingBackend.js +115 -14
- package/node_modules/playwright/lib/mcp/test/skyRampExport.js +13 -1
- package/node_modules/playwright/node_modules/playwright-core/.DS_Store +0 -0
- package/node_modules/playwright/node_modules/playwright-core/lib/server/codegen/skyramp/jsonlReader.js +2 -0
- package/node_modules/playwright/node_modules/playwright-core/lib/vite/htmlReport/index.html +27 -253
- package/node_modules/playwright/node_modules/playwright-core/lib/vite/recorder/assets/{codeMirrorModule-DtudTj_v.js → codeMirrorModule-DJMC4zNo.js} +1 -1
- package/node_modules/playwright/node_modules/playwright-core/lib/vite/recorder/assets/index-BW82eAUI.js +196 -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-FNMuBzX1.js → codeMirrorModule-CZfp96qZ.js} +1 -1
- package/node_modules/playwright/node_modules/playwright-core/lib/vite/traceViewer/assets/defaultSettingsView-gpLo02E0.js +809 -0
- package/node_modules/playwright/node_modules/playwright-core/lib/vite/traceViewer/index.Bq1r1URj.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.VEfqi1qN.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/server/codegen/skyramp/jsonlReader.ts +1 -1
- package/node_modules/playwright/package.json +1 -1
- package/package.json +2 -2
- package/node_modules/playwright/node_modules/playwright-core/lib/vite/recorder/assets/index-BpDwp16L.js +0 -422
- package/node_modules/playwright/node_modules/playwright-core/lib/vite/traceViewer/assets/defaultSettingsView-Co9upU5h.js +0 -1035
- package/node_modules/playwright/node_modules/playwright-core/lib/vite/traceViewer/index.DXNIQ_dx.js +0 -2
- package/node_modules/playwright/node_modules/playwright-core/lib/vite/traceViewer/uiMode.CIKB3XSv.js +0 -5
|
@@ -28,16 +28,20 @@ const assertToolSchema = {
|
|
|
28
28
|
name: "browser_assert",
|
|
29
29
|
title: "Assert element state",
|
|
30
30
|
description: [
|
|
31
|
-
"Assert that an element has expected text, value, or
|
|
31
|
+
"Assert that an element has expected text, value, or state.",
|
|
32
32
|
"Use this after key actions to verify the page state is correct.",
|
|
33
33
|
"The assertion is recorded in the trace for test generation.",
|
|
34
|
-
'Types: "text" checks element text content, "value" checks input field value
|
|
34
|
+
'Types: "text" checks element text content, "value" checks input field value,',
|
|
35
|
+
'"checked" checks checkbox/radio state, "disabled" checks disabled/enabled state,',
|
|
36
|
+
'"visible" checks the element is visible,',
|
|
37
|
+
`"count" counts elements sharing the referenced element's role (or exact text)`,
|
|
38
|
+
"across the page, e.g. ref one option of an open dropdown to assert the number of options."
|
|
35
39
|
].join(" "),
|
|
36
40
|
inputSchema: import_mcpBundle.z.object({
|
|
37
|
-
type: import_mcpBundle.z.enum(["text", "value"]).describe(
|
|
38
|
-
ref: import_mcpBundle.z.string().describe(
|
|
41
|
+
type: import_mcpBundle.z.enum(["text", "value", "checked", "disabled", "visible", "count"]).describe(`Type of assertion: "text" for text content, "value" for input field value, "checked" for checkbox/radio state, "disabled" for disabled/enabled state ("false" asserts the element is enabled), "visible" for element visibility, "count" for the number of elements with the referenced element's role`),
|
|
42
|
+
ref: import_mcpBundle.z.string().describe('Element reference from the latest snapshot. For "count", reference one exemplar element (e.g. one option in an open dropdown).'),
|
|
39
43
|
element: import_mcpBundle.z.string().describe("Human-readable description of the element being asserted"),
|
|
40
|
-
expected: import_mcpBundle.z.string().describe(
|
|
44
|
+
expected: import_mcpBundle.z.string().optional().describe('Expected text or value ("text"/"value"), "true"/"false" ("checked"/"disabled", default "true"; "disabled" with "false" asserts enabled), the expected element count ("count"). Not used for "visible".'),
|
|
41
45
|
substring: import_mcpBundle.z.boolean().optional().default(true).describe("For text assertions: match as substring (true) or exact match (false)")
|
|
42
46
|
}),
|
|
43
47
|
type: "readOnly"
|
|
@@ -370,6 +370,22 @@ async function performClientAction(page, actionInContext) {
|
|
|
370
370
|
throw new Error(`assertVisible failed: element is not visible`);
|
|
371
371
|
return;
|
|
372
372
|
}
|
|
373
|
+
case "assertDisabled": {
|
|
374
|
+
if (typeof action.disabled !== "boolean")
|
|
375
|
+
throw new Error(`assertDisabled failed: trace line has no boolean "disabled" field`);
|
|
376
|
+
const disabled = await locator.isDisabled(opts);
|
|
377
|
+
if (disabled !== action.disabled)
|
|
378
|
+
throw new Error(`assertDisabled failed: expected disabled=${action.disabled}, got ${disabled}`);
|
|
379
|
+
return;
|
|
380
|
+
}
|
|
381
|
+
case "assertCount": {
|
|
382
|
+
await locator.first().waitFor(opts).catch(() => {
|
|
383
|
+
});
|
|
384
|
+
const count = await locator.count();
|
|
385
|
+
if (count !== action.count)
|
|
386
|
+
throw new Error(`assertCount failed: expected ${action.count} element(s), got ${count}`);
|
|
387
|
+
return;
|
|
388
|
+
}
|
|
373
389
|
default:
|
|
374
390
|
return;
|
|
375
391
|
}
|
|
@@ -855,6 +855,14 @@ Continue recording with browser_* tools, then call skyramp_export_zip to write t
|
|
|
855
855
|
return { toolName: "browser_assert", code: `assertChecked:${a.selector}:${!!a.checked}`, args: { type: "checked", selector: a.selector, checked: !!a.checked } };
|
|
856
856
|
case "assertVisible":
|
|
857
857
|
return { toolName: "browser_assert", code: `assertVisible:${a.selector}`, args: { type: "visible", selector: a.selector } };
|
|
858
|
+
case "assertDisabled":
|
|
859
|
+
if (typeof a.disabled !== "boolean")
|
|
860
|
+
return null;
|
|
861
|
+
return { toolName: "browser_assert", code: `assertDisabled:${a.selector}:${a.disabled}`, args: { type: "disabled", selector: a.selector, disabled: a.disabled } };
|
|
862
|
+
case "assertCount":
|
|
863
|
+
if (typeof a.count !== "number" || !Number.isInteger(a.count) || a.count < 0)
|
|
864
|
+
return null;
|
|
865
|
+
return { toolName: "browser_assert", code: `assertCount:${a.selector}:${a.count}`, args: { type: "count", selector: a.selector, count: a.count } };
|
|
858
866
|
case "visualSnapshot": {
|
|
859
867
|
const args = this._seedVisualSnapshotArgs(a);
|
|
860
868
|
return args ? { toolName: "browser_visual_snapshot", code: "", args } : null;
|
|
@@ -1260,8 +1268,13 @@ Linked GoJS nodes ${params.fromKey} \u2192 ${params.toKey} (${params.fromPort}\u
|
|
|
1260
1268
|
*/
|
|
1261
1269
|
async _handleAssert(params) {
|
|
1262
1270
|
const timestamp = Date.now();
|
|
1263
|
-
if (!params.expected)
|
|
1271
|
+
if (!params.expected && (params.type === "text" || params.type === "value" || params.type === "count"))
|
|
1264
1272
|
return { content: [{ type: "text", text: '### Error\n"expected" parameter is required.' }], isError: true };
|
|
1273
|
+
if (params.type === "count" && !/^\d+$/.test(params.expected))
|
|
1274
|
+
return { content: [{ type: "text", text: '### Error\nFor "count" assertions, "expected" must be a non-negative integer.' }], isError: true };
|
|
1275
|
+
if ((params.type === "checked" || params.type === "disabled") && params.expected !== void 0 && params.expected !== "true" && params.expected !== "false")
|
|
1276
|
+
return { content: [{ type: "text", text: `### Error
|
|
1277
|
+
For "${params.type}" assertions, "expected" must be "true" or "false".` }], isError: true };
|
|
1265
1278
|
const hoverResult = await this._browserBackend.callTool("browser_hover", {
|
|
1266
1279
|
element: params.element,
|
|
1267
1280
|
ref: params.ref
|
|
@@ -1312,30 +1325,105 @@ Could not parse locator: ${locatorExpr}` }], isError: true };
|
|
|
1312
1325
|
}
|
|
1313
1326
|
let passed = false;
|
|
1314
1327
|
let details = "";
|
|
1328
|
+
let countSelector = "";
|
|
1315
1329
|
if (params.type === "text") {
|
|
1316
1330
|
passed = elementText.includes(params.expected) || elementValue.includes(params.expected);
|
|
1317
1331
|
details = passed ? `Text assertion passed: "${params.element}" contains "${params.expected}".` : `Text assertion FAILED: "${params.element}" has text "${elementText}"${elementValue ? ` / value "${elementValue}"` : ""}, expected "${params.expected}".`;
|
|
1318
|
-
} else {
|
|
1332
|
+
} else if (params.type === "value") {
|
|
1319
1333
|
passed = elementValue === params.expected || elementValue.includes(params.expected);
|
|
1320
1334
|
details = passed ? `Value assertion passed: "${params.element}" has value "${params.expected}".` : `Value assertion FAILED: "${params.element}" has value "${elementValue}", expected "${params.expected}".`;
|
|
1335
|
+
} else if (params.type === "checked" || params.type === "disabled") {
|
|
1336
|
+
if (!refLine) {
|
|
1337
|
+
passed = false;
|
|
1338
|
+
details = `${params.type === "checked" ? "Checked" : "Disabled"} assertion FAILED: "${params.element}" (ref=${params.ref}) is not in the current snapshot \u2014 re-snapshot and retry with a fresh ref.`;
|
|
1339
|
+
} else if (params.type === "checked") {
|
|
1340
|
+
const expectChecked = params.expected !== "false";
|
|
1341
|
+
const isChecked = refLine.includes("[checked]");
|
|
1342
|
+
const isMixed = refLine.includes("[checked=mixed]");
|
|
1343
|
+
passed = expectChecked ? isChecked : !isChecked && !isMixed;
|
|
1344
|
+
details = passed ? `Checked assertion passed: "${params.element}" is ${expectChecked ? "checked" : "unchecked"}.` : `Checked assertion FAILED: "${params.element}" is ${isMixed ? "mixed" : isChecked ? "checked" : "unchecked"}, expected ${expectChecked ? "checked" : "unchecked"}.`;
|
|
1345
|
+
} else {
|
|
1346
|
+
const expectDisabled = params.expected !== "false";
|
|
1347
|
+
const isDisabled = refLine.includes("[disabled]");
|
|
1348
|
+
passed = expectDisabled === isDisabled;
|
|
1349
|
+
details = passed ? `Disabled assertion passed: "${params.element}" is ${expectDisabled ? "disabled" : "enabled"}.` : `Disabled assertion FAILED: "${params.element}" is ${isDisabled ? "disabled" : "enabled"}, expected ${expectDisabled ? "disabled" : "enabled"}.`;
|
|
1350
|
+
}
|
|
1351
|
+
} else if (params.type === "visible") {
|
|
1352
|
+
passed = !!refLine;
|
|
1353
|
+
details = passed ? `Visibility assertion passed: "${params.element}" is visible.` : `Visibility assertion FAILED: "${params.element}" (ref=${params.ref}) is not in the current snapshot.`;
|
|
1354
|
+
} else {
|
|
1355
|
+
const refRole = (parsed.locator.kind === "role" ? String(parsed.locator.body) : "") || refLine.match(/^\s*-\s*(\w+)/)?.[1] || "";
|
|
1356
|
+
if (!refRole) {
|
|
1357
|
+
return { content: [{ type: "text", text: `### Assertion Failed
|
|
1358
|
+
Could not determine the role of ref=${params.ref} for a count assertion.` }], isError: true };
|
|
1359
|
+
}
|
|
1360
|
+
if (!TraceRecordingBackend.COUNTABLE_ROLES.has(refRole)) {
|
|
1361
|
+
return { content: [{ type: "text", text: `### Assertion Failed
|
|
1362
|
+
Role "${refRole}" is not countable \u2014 reference an exemplar with a semantic role (listitem, option, row, button, link, \u2026).` }], isError: true };
|
|
1363
|
+
}
|
|
1364
|
+
const tab = this._browserBackend.context?.currentTab();
|
|
1365
|
+
if (!tab) {
|
|
1366
|
+
return { content: [{ type: "text", text: "### Assertion Failed\nNo active page." }], isError: true };
|
|
1367
|
+
}
|
|
1368
|
+
countSelector = `internal:role=${refRole}`;
|
|
1369
|
+
let countActual;
|
|
1370
|
+
try {
|
|
1371
|
+
countActual = await tab.page.getByRole(refRole).count();
|
|
1372
|
+
} catch (e) {
|
|
1373
|
+
return { content: [{ type: "text", text: `### Assertion Failed
|
|
1374
|
+
Could not count "${refRole}" elements: ${e.message}` }], isError: true };
|
|
1375
|
+
}
|
|
1376
|
+
passed = countActual === Number(params.expected);
|
|
1377
|
+
details = passed ? `Count assertion passed: ${countActual} "${refRole}" element(s) on the page.` : `Count assertion FAILED: found ${countActual} "${refRole}" element(s), expected ${params.expected}.`;
|
|
1321
1378
|
}
|
|
1322
1379
|
if (passed) {
|
|
1323
1380
|
const recordedExpected = params.type === "text" && params.substring !== false ? (0, import_utils.stableTextLead)(params.expected) : params.expected;
|
|
1324
1381
|
const selectorText = parsed.locator.kind === "text" ? parsed.locator.body : parsed.locator.next?.kind === "text" ? parsed.locator.next.body : parsed.locator.options?.name ?? null;
|
|
1325
|
-
|
|
1382
|
+
const tautological = Boolean((params.type === "text" || params.type === "value") && selectorText && selectorText === recordedExpected);
|
|
1383
|
+
if (tautological) {
|
|
1326
1384
|
traceDebug(`Skipped tautological assertion: selector already matches "${recordedExpected}"`);
|
|
1327
1385
|
} else {
|
|
1328
|
-
|
|
1329
|
-
|
|
1330
|
-
|
|
1331
|
-
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
|
|
1338
|
-
|
|
1386
|
+
let assertName;
|
|
1387
|
+
let args;
|
|
1388
|
+
let code;
|
|
1389
|
+
switch (params.type) {
|
|
1390
|
+
case "checked": {
|
|
1391
|
+
const expectChecked = params.expected !== "false";
|
|
1392
|
+
assertName = "assertChecked";
|
|
1393
|
+
args = { type: "checked", ref: params.ref, selector: parsed.selector, checked: expectChecked };
|
|
1394
|
+
code = `assertChecked:${parsed.selector}:${expectChecked}`;
|
|
1395
|
+
break;
|
|
1396
|
+
}
|
|
1397
|
+
case "disabled": {
|
|
1398
|
+
const expectDisabled = params.expected !== "false";
|
|
1399
|
+
assertName = "assertDisabled";
|
|
1400
|
+
args = { type: "disabled", ref: params.ref, selector: parsed.selector, disabled: expectDisabled };
|
|
1401
|
+
code = `assertDisabled:${parsed.selector}:${expectDisabled}`;
|
|
1402
|
+
break;
|
|
1403
|
+
}
|
|
1404
|
+
case "visible":
|
|
1405
|
+
assertName = "assertVisible";
|
|
1406
|
+
args = { type: "visible", ref: params.ref, selector: parsed.selector };
|
|
1407
|
+
code = `assertVisible:${parsed.selector}`;
|
|
1408
|
+
break;
|
|
1409
|
+
case "count":
|
|
1410
|
+
assertName = "assertCount";
|
|
1411
|
+
args = { type: "count", ref: params.ref, selector: countSelector, count: Number(params.expected) };
|
|
1412
|
+
code = `assertCount:${countSelector}:${params.expected}`;
|
|
1413
|
+
break;
|
|
1414
|
+
case "value":
|
|
1415
|
+
assertName = "assertValue";
|
|
1416
|
+
args = { type: "value", ref: params.ref, selector: parsed.selector, expected: recordedExpected };
|
|
1417
|
+
code = `assertValue:${parsed.selector}:${recordedExpected}`;
|
|
1418
|
+
break;
|
|
1419
|
+
default:
|
|
1420
|
+
assertName = "assertText";
|
|
1421
|
+
args = { type: "text", ref: params.ref, selector: parsed.selector, expected: recordedExpected, substring: params.substring };
|
|
1422
|
+
code = `assertText:${parsed.selector}:${recordedExpected}:${params.substring}`;
|
|
1423
|
+
break;
|
|
1424
|
+
}
|
|
1425
|
+
this._trackedActions.push({ toolName: "browser_assert", args, code, timestamp });
|
|
1426
|
+
traceDebug(`Assert: ${assertName} with selector ${args.selector}`);
|
|
1339
1427
|
}
|
|
1340
1428
|
}
|
|
1341
1429
|
return {
|
|
@@ -1570,6 +1658,19 @@ Cell row ${params.row}, column ${params.column} has "${actual}", expected "${par
|
|
|
1570
1658
|
"menuitem"
|
|
1571
1659
|
]);
|
|
1572
1660
|
}
|
|
1661
|
+
static {
|
|
1662
|
+
// Roles browser_assert type:"count" may generalize page-wide. Superset of
|
|
1663
|
+
// ASSERTABLE_ROLES (kept separate — that set is for name-bearing
|
|
1664
|
+
// single-element selector extraction; widening one must not loosen the
|
|
1665
|
+
// other) plus the collection-item roles that are the natural count targets.
|
|
1666
|
+
this.COUNTABLE_ROLES = /* @__PURE__ */ new Set([
|
|
1667
|
+
...TraceRecordingBackend.ASSERTABLE_ROLES,
|
|
1668
|
+
"listitem",
|
|
1669
|
+
"article",
|
|
1670
|
+
"gridcell",
|
|
1671
|
+
"treeitem"
|
|
1672
|
+
]);
|
|
1673
|
+
}
|
|
1573
1674
|
_extractLocatorForRef(refLine) {
|
|
1574
1675
|
const testidMatch = refLine.match(/testid="([^"]+)"/);
|
|
1575
1676
|
if (testidMatch) {
|
|
@@ -437,6 +437,10 @@ function assertActionToJsonl(action, pageGuid, timestamp) {
|
|
|
437
437
|
expected = args.expected ?? "";
|
|
438
438
|
} else if (assertType === "checked") {
|
|
439
439
|
expected = String(args.checked === true || args.expected === "true" || args.expected === true);
|
|
440
|
+
} else if (assertType === "disabled") {
|
|
441
|
+
expected = String(args.disabled === true || args.expected === "true" || args.expected === true);
|
|
442
|
+
} else if (assertType === "count") {
|
|
443
|
+
expected = String(args.count ?? args.expected ?? "");
|
|
440
444
|
}
|
|
441
445
|
} else {
|
|
442
446
|
const parts = code.split(":");
|
|
@@ -446,7 +450,7 @@ function assertActionToJsonl(action, pageGuid, timestamp) {
|
|
|
446
450
|
substring = parts[parts.length - 1] === "true";
|
|
447
451
|
expected = parts[parts.length - 2] || "";
|
|
448
452
|
selector = parts.slice(1, parts.length - 2).join(":");
|
|
449
|
-
} else if (assertType === "value" || assertType === "checked") {
|
|
453
|
+
} else if (assertType === "value" || assertType === "checked" || assertType === "disabled" || assertType === "count") {
|
|
450
454
|
expected = parts[parts.length - 1] || "";
|
|
451
455
|
selector = parts.slice(1, parts.length - 1).join(":");
|
|
452
456
|
}
|
|
@@ -461,6 +465,14 @@ function assertActionToJsonl(action, pageGuid, timestamp) {
|
|
|
461
465
|
return JSON.stringify({ name: "assertValue", selector, value: expected, locator, ...base });
|
|
462
466
|
case "checked":
|
|
463
467
|
return JSON.stringify({ name: "assertChecked", selector, checked: expected === "true", locator, ...base });
|
|
468
|
+
case "disabled":
|
|
469
|
+
return JSON.stringify({ name: "assertDisabled", selector, disabled: expected === "true", locator, ...base });
|
|
470
|
+
case "count": {
|
|
471
|
+
const count = Number(expected);
|
|
472
|
+
if (!/^\d+$/.test(expected) || !Number.isInteger(count))
|
|
473
|
+
return null;
|
|
474
|
+
return JSON.stringify({ name: "assertCount", selector, count, locator, ...base });
|
|
475
|
+
}
|
|
464
476
|
default:
|
|
465
477
|
return null;
|
|
466
478
|
}
|
|
Binary file
|