@vijaypjavvadi/bdd2pw 1.0.0

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 (83) hide show
  1. package/CHANGELOG.md +308 -0
  2. package/LICENSE +21 -0
  3. package/README.md +254 -0
  4. package/dist/cli.d.ts +6 -0
  5. package/dist/cli.js +176 -0
  6. package/dist/cli.js.map +1 -0
  7. package/dist/discovery/mcpClient.d.ts +53 -0
  8. package/dist/discovery/mcpClient.js +326 -0
  9. package/dist/discovery/mcpClient.js.map +1 -0
  10. package/dist/discovery/snapshotParser.d.ts +13 -0
  11. package/dist/discovery/snapshotParser.js +51 -0
  12. package/dist/discovery/snapshotParser.js.map +1 -0
  13. package/dist/emitters/facade.d.ts +52 -0
  14. package/dist/emitters/facade.js +125 -0
  15. package/dist/emitters/facade.js.map +1 -0
  16. package/dist/http/artifacts.d.ts +14 -0
  17. package/dist/http/artifacts.js +36 -0
  18. package/dist/http/artifacts.js.map +1 -0
  19. package/dist/http/jobs.d.ts +26 -0
  20. package/dist/http/jobs.js +59 -0
  21. package/dist/http/jobs.js.map +1 -0
  22. package/dist/http/routes.d.ts +11 -0
  23. package/dist/http/routes.js +267 -0
  24. package/dist/http/routes.js.map +1 -0
  25. package/dist/http/schemas.d.ts +167 -0
  26. package/dist/http/schemas.js +57 -0
  27. package/dist/http/schemas.js.map +1 -0
  28. package/dist/index.d.ts +49 -0
  29. package/dist/index.js +507 -0
  30. package/dist/index.js.map +1 -0
  31. package/dist/llm/governanceClient.d.ts +24 -0
  32. package/dist/llm/governanceClient.js +22 -0
  33. package/dist/llm/governanceClient.js.map +1 -0
  34. package/dist/llm/providers/anthropic.d.ts +8 -0
  35. package/dist/llm/providers/anthropic.js +12 -0
  36. package/dist/llm/providers/anthropic.js.map +1 -0
  37. package/dist/llm/providers/gemini.d.ts +5 -0
  38. package/dist/llm/providers/gemini.js +9 -0
  39. package/dist/llm/providers/gemini.js.map +1 -0
  40. package/dist/llm/providers/openai.d.ts +5 -0
  41. package/dist/llm/providers/openai.js +9 -0
  42. package/dist/llm/providers/openai.js.map +1 -0
  43. package/dist/parser/gherkinParser.d.ts +17 -0
  44. package/dist/parser/gherkinParser.js +143 -0
  45. package/dist/parser/gherkinParser.js.map +1 -0
  46. package/dist/repo/projectScaffolder.d.ts +24 -0
  47. package/dist/repo/projectScaffolder.js +64 -0
  48. package/dist/repo/projectScaffolder.js.map +1 -0
  49. package/dist/repo/repoScanner.d.ts +21 -0
  50. package/dist/repo/repoScanner.js +132 -0
  51. package/dist/repo/repoScanner.js.map +1 -0
  52. package/dist/reports/reviewReport.d.ts +19 -0
  53. package/dist/reports/reviewReport.js +96 -0
  54. package/dist/reports/reviewReport.js.map +1 -0
  55. package/dist/server.d.ts +10 -0
  56. package/dist/server.js +40 -0
  57. package/dist/server.js.map +1 -0
  58. package/dist/transformers/locatorPicker.d.ts +40 -0
  59. package/dist/transformers/locatorPicker.js +283 -0
  60. package/dist/transformers/locatorPicker.js.map +1 -0
  61. package/dist/transformers/pomResolver.d.ts +28 -0
  62. package/dist/transformers/pomResolver.js +35 -0
  63. package/dist/transformers/pomResolver.js.map +1 -0
  64. package/dist/transformers/stepMatcher.d.ts +36 -0
  65. package/dist/transformers/stepMatcher.js +461 -0
  66. package/dist/transformers/stepMatcher.js.map +1 -0
  67. package/dist/types.d.ts +166 -0
  68. package/dist/types.js +7 -0
  69. package/dist/types.js.map +1 -0
  70. package/dist/utils/logger.d.ts +6 -0
  71. package/dist/utils/logger.js +25 -0
  72. package/dist/utils/logger.js.map +1 -0
  73. package/dist/utils/naming.d.ts +23 -0
  74. package/dist/utils/naming.js +58 -0
  75. package/dist/utils/naming.js.map +1 -0
  76. package/dist/validate/tscRunner.d.ts +17 -0
  77. package/dist/validate/tscRunner.js +98 -0
  78. package/dist/validate/tscRunner.js.map +1 -0
  79. package/package.json +69 -0
  80. package/templates/gitignore.tmpl +10 -0
  81. package/templates/package.json.tmpl +16 -0
  82. package/templates/playwright.config.ts.tmpl +24 -0
  83. package/templates/tsconfig.json.tmpl +15 -0
package/dist/cli.js ADDED
@@ -0,0 +1,176 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+ /**
4
+ * Commander CLI entry. Thin shell over `src/index.ts`.
5
+ * See docs/SCOPE.md §8.
6
+ */
7
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
8
+ if (k2 === undefined) k2 = k;
9
+ var desc = Object.getOwnPropertyDescriptor(m, k);
10
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
11
+ desc = { enumerable: true, get: function() { return m[k]; } };
12
+ }
13
+ Object.defineProperty(o, k2, desc);
14
+ }) : (function(o, m, k, k2) {
15
+ if (k2 === undefined) k2 = k;
16
+ o[k2] = m[k];
17
+ }));
18
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
19
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
20
+ }) : function(o, v) {
21
+ o["default"] = v;
22
+ });
23
+ var __importStar = (this && this.__importStar) || (function () {
24
+ var ownKeys = function(o) {
25
+ ownKeys = Object.getOwnPropertyNames || function (o) {
26
+ var ar = [];
27
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
28
+ return ar;
29
+ };
30
+ return ownKeys(o);
31
+ };
32
+ return function (mod) {
33
+ if (mod && mod.__esModule) return mod;
34
+ var result = {};
35
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
36
+ __setModuleDefault(result, mod);
37
+ return result;
38
+ };
39
+ })();
40
+ Object.defineProperty(exports, "__esModule", { value: true });
41
+ const commander_1 = require("commander");
42
+ const path = __importStar(require("path"));
43
+ const index_1 = require("./index");
44
+ const logger_1 = require("./utils/logger");
45
+ const program = new commander_1.Command();
46
+ program
47
+ .name("bdd2pw")
48
+ .description("Scaffold runnable Playwright TypeScript tests from Gherkin .feature files.")
49
+ .version(require("../package.json").version);
50
+ program
51
+ .command("scaffold")
52
+ .description("Read a .feature file + URL, emit Page Objects + spec into a Playwright TS repo.")
53
+ .argument("<feature>", "Path to a Gherkin .feature file")
54
+ .requiredOption("--url <url>", "URL of the page under test")
55
+ .requiredOption("--page <Name>", "Page Object class name (PascalCase)")
56
+ .requiredOption("--repo <dir>", "Target Playwright TS repo (created if missing)")
57
+ .option("--pages <list>", "Comma-separated list of additional Page Object names")
58
+ .option("--storage-state <path>", "Pre-authenticated storage state JSON")
59
+ .option("--headed", "Show browser during MCP scan", false)
60
+ .option("--snapshot-file <path>", "Read elements from a JSON file instead of launching a browser")
61
+ .option("--no-discovery", "Skip page discovery entirely (rule-only probing). Field-referencing rules will fall to TODO.")
62
+ .option("--llm <provider>", "Enable LLM fallback: anthropic | openai | gemini")
63
+ .option("--governance-url <url>", "ai-governance sidecar URL", "http://localhost:8004")
64
+ .option("--templates <dir>", "Override default project template directory")
65
+ .option("--dry-run", "Print plan, write nothing", false)
66
+ .option("--no-validate", "Skip tsc --noEmit step")
67
+ .option("--telemetry", "Enable local SQLite failure telemetry", false)
68
+ .option("--force", "Overwrite existing spec files", false)
69
+ .action(async (feature, opts) => {
70
+ try {
71
+ const result = await (0, index_1.scaffold)({
72
+ feature: path.resolve(feature),
73
+ url: opts.url,
74
+ page: opts.page,
75
+ repo: path.resolve(opts.repo),
76
+ pages: opts.pages?.split(",").map((s) => s.trim()),
77
+ storageState: opts.storageState,
78
+ headed: opts.headed,
79
+ llm: opts.llm,
80
+ governanceUrl: opts.governanceUrl,
81
+ templates: opts.templates,
82
+ dryRun: opts.dryRun,
83
+ noValidate: opts.noValidate === false,
84
+ telemetry: opts.telemetry,
85
+ force: opts.force,
86
+ snapshotFile: opts.snapshotFile,
87
+ // Commander negates --no-discovery into opts.discovery=false
88
+ noDiscovery: opts.discovery === false,
89
+ });
90
+ logger_1.logger.info({ result }, "scaffold complete");
91
+ }
92
+ catch (err) {
93
+ logger_1.logger.error({ err }, "scaffold failed");
94
+ process.exit(exitCodeFor(err));
95
+ }
96
+ });
97
+ program
98
+ .command("analyze")
99
+ .description("Dry-run: parse .feature, scan URL, print locator + binding preview.")
100
+ .argument("<feature>", "Path to a Gherkin .feature file")
101
+ .requiredOption("--url <url>", "URL to scan")
102
+ .option("--storage-state <path>", "Pre-authenticated storage state JSON")
103
+ .option("--headed", "Show browser during MCP scan", false)
104
+ .action(async (feature, opts) => {
105
+ try {
106
+ const result = await (0, index_1.analyze)({
107
+ feature: path.resolve(feature),
108
+ url: opts.url,
109
+ storageState: opts.storageState,
110
+ headed: opts.headed,
111
+ });
112
+ console.log(JSON.stringify(result, null, 2));
113
+ }
114
+ catch (err) {
115
+ logger_1.logger.error({ err }, "analyze failed");
116
+ process.exit(exitCodeFor(err));
117
+ }
118
+ });
119
+ program
120
+ .command("update-pom")
121
+ .description("Re-scan a URL and merge new locators into an existing Page Object.")
122
+ .requiredOption("--page <Name>", "Page Object class name")
123
+ .requiredOption("--url <url>", "URL to re-scan")
124
+ .requiredOption("--repo <dir>", "Existing Playwright TS repo")
125
+ .option("--storage-state <path>", "Pre-authenticated storage state JSON")
126
+ .option("--headed", "Show browser during MCP scan", false)
127
+ .option("--templates <dir>", "Override default project template directory")
128
+ .action(async (opts) => {
129
+ try {
130
+ const result = await (0, index_1.updatePom)({
131
+ page: opts.page,
132
+ url: opts.url,
133
+ repo: path.resolve(opts.repo),
134
+ storageState: opts.storageState,
135
+ headed: opts.headed,
136
+ templates: opts.templates,
137
+ });
138
+ logger_1.logger.info({ result }, "update-pom complete");
139
+ }
140
+ catch (err) {
141
+ logger_1.logger.error({ err }, "update-pom failed");
142
+ process.exit(exitCodeFor(err));
143
+ }
144
+ });
145
+ program
146
+ .command("serve")
147
+ .description("Start the bdd2pw HTTP service.")
148
+ .option("--port <port>", "Port to listen on", "4300")
149
+ .action(async (opts) => {
150
+ const { startServer } = await Promise.resolve().then(() => __importStar(require("./server")));
151
+ await startServer({ port: Number(opts.port) });
152
+ });
153
+ /**
154
+ * Map error → CLI exit code. See docs/ARCHITECTURE.md §7.
155
+ */
156
+ function exitCodeFor(err) {
157
+ const name = err?.name ?? "";
158
+ switch (name) {
159
+ case "GherkinParseError":
160
+ return 2;
161
+ case "FileSystemError":
162
+ return 3;
163
+ case "McpError":
164
+ case "GovernanceError":
165
+ return 4;
166
+ case "EmitterConsistencyError":
167
+ return 5;
168
+ default:
169
+ return 1;
170
+ }
171
+ }
172
+ program.parseAsync(process.argv).catch((err) => {
173
+ logger_1.logger.error({ err }, "fatal");
174
+ process.exit(1);
175
+ });
176
+ //# sourceMappingURL=cli.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,53 @@
1
+ /**
2
+ * Page discovery client.
3
+ *
4
+ * Two implementations behind one entrypoint:
5
+ * 1. **Real browser** (Phase 1b, this file). Launches Chromium via
6
+ * `playwright`, navigates to the URL, walks the DOM, returns a flat
7
+ * `ElementIR[]`-shaped accessibility list.
8
+ * 2. **File snapshot** (Phase 1a fallback). Reads a pre-captured JSON
9
+ * snapshot when `opts.snapshotFile` is set. Useful for offline / CI /
10
+ * no-network tests, and for scenarios where you've captured a known-good
11
+ * page state and don't want to re-scan every run.
12
+ *
13
+ * The orchestrator picks based on whether `snapshotFile` is set. Both
14
+ * implementations return the same `PageSnapshot` shape so `parseSnapshot`
15
+ * downstream is unaware of the choice.
16
+ *
17
+ * **AQ-2 resolved:** uses `playwright` directly, not `@playwright/mcp`.
18
+ * MCP is an LLM-control protocol; we just need a one-shot programmatic
19
+ * browser. Direct Playwright is one fewer process, one fewer protocol
20
+ * layer, no JSON-RPC overhead. The MCP framing only made sense if we were
21
+ * letting an LLM drive the browser interactively — we're not.
22
+ *
23
+ * **Optional dependency.** `playwright` is in `optionalDependencies`. The
24
+ * scanner imports it dynamically and surfaces a clear install hint in
25
+ * `McpError` if it's missing.
26
+ */
27
+ export declare class McpError extends Error {
28
+ readonly cause?: unknown | undefined;
29
+ constructor(message: string, cause?: unknown | undefined);
30
+ }
31
+ export interface ScanPageOptions {
32
+ url: string;
33
+ /** Pre-authenticated storage state JSON path (Playwright format). */
34
+ storageState?: string;
35
+ /** Show the browser window. Default: headless. */
36
+ headed?: boolean;
37
+ /**
38
+ * If set, read snapshot from this JSON file instead of launching a
39
+ * browser. Used by tests, CI without network, and pinned-fixture runs.
40
+ */
41
+ snapshotFile?: string;
42
+ /** ms to wait for `networkidle` after navigation. Default: 5000. */
43
+ waitForNetworkIdleMs?: number;
44
+ }
45
+ export interface PageSnapshot {
46
+ url: string;
47
+ title: string;
48
+ /** Flat list of element descriptors (snapshotParser handles flat-array shape). */
49
+ accessibilityTree: unknown;
50
+ /** Raw outer HTML of <body>, length-capped — used by some downstream heuristics. */
51
+ domSnapshot: string;
52
+ }
53
+ export declare function scanPage(opts: ScanPageOptions): Promise<PageSnapshot>;
@@ -0,0 +1,326 @@
1
+ "use strict";
2
+ /// <reference lib="dom" />
3
+ /**
4
+ * Page discovery client.
5
+ *
6
+ * Two implementations behind one entrypoint:
7
+ * 1. **Real browser** (Phase 1b, this file). Launches Chromium via
8
+ * `playwright`, navigates to the URL, walks the DOM, returns a flat
9
+ * `ElementIR[]`-shaped accessibility list.
10
+ * 2. **File snapshot** (Phase 1a fallback). Reads a pre-captured JSON
11
+ * snapshot when `opts.snapshotFile` is set. Useful for offline / CI /
12
+ * no-network tests, and for scenarios where you've captured a known-good
13
+ * page state and don't want to re-scan every run.
14
+ *
15
+ * The orchestrator picks based on whether `snapshotFile` is set. Both
16
+ * implementations return the same `PageSnapshot` shape so `parseSnapshot`
17
+ * downstream is unaware of the choice.
18
+ *
19
+ * **AQ-2 resolved:** uses `playwright` directly, not `@playwright/mcp`.
20
+ * MCP is an LLM-control protocol; we just need a one-shot programmatic
21
+ * browser. Direct Playwright is one fewer process, one fewer protocol
22
+ * layer, no JSON-RPC overhead. The MCP framing only made sense if we were
23
+ * letting an LLM drive the browser interactively — we're not.
24
+ *
25
+ * **Optional dependency.** `playwright` is in `optionalDependencies`. The
26
+ * scanner imports it dynamically and surfaces a clear install hint in
27
+ * `McpError` if it's missing.
28
+ */
29
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
30
+ if (k2 === undefined) k2 = k;
31
+ var desc = Object.getOwnPropertyDescriptor(m, k);
32
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
33
+ desc = { enumerable: true, get: function() { return m[k]; } };
34
+ }
35
+ Object.defineProperty(o, k2, desc);
36
+ }) : (function(o, m, k, k2) {
37
+ if (k2 === undefined) k2 = k;
38
+ o[k2] = m[k];
39
+ }));
40
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
41
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
42
+ }) : function(o, v) {
43
+ o["default"] = v;
44
+ });
45
+ var __importStar = (this && this.__importStar) || (function () {
46
+ var ownKeys = function(o) {
47
+ ownKeys = Object.getOwnPropertyNames || function (o) {
48
+ var ar = [];
49
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
50
+ return ar;
51
+ };
52
+ return ownKeys(o);
53
+ };
54
+ return function (mod) {
55
+ if (mod && mod.__esModule) return mod;
56
+ var result = {};
57
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
58
+ __setModuleDefault(result, mod);
59
+ return result;
60
+ };
61
+ })();
62
+ Object.defineProperty(exports, "__esModule", { value: true });
63
+ exports.McpError = void 0;
64
+ exports.scanPage = scanPage;
65
+ const fs = __importStar(require("fs-extra"));
66
+ class McpError extends Error {
67
+ constructor(message, cause) {
68
+ super(message);
69
+ this.cause = cause;
70
+ this.name = "McpError";
71
+ }
72
+ }
73
+ exports.McpError = McpError;
74
+ async function scanPage(opts) {
75
+ if (opts.snapshotFile) {
76
+ return scanPageFromFile(opts.snapshotFile);
77
+ }
78
+ return scanPageWithBrowser(opts);
79
+ }
80
+ // ─── File-snapshot fallback ────────────────────────────────────────────────
81
+ async function scanPageFromFile(snapshotFile) {
82
+ if (!(await fs.pathExists(snapshotFile))) {
83
+ throw new McpError(`Snapshot file not found: ${snapshotFile}`);
84
+ }
85
+ let parsed;
86
+ try {
87
+ parsed = await fs.readJSON(snapshotFile);
88
+ }
89
+ catch (err) {
90
+ throw new McpError(`Could not parse snapshot JSON: ${snapshotFile}`, err);
91
+ }
92
+ return {
93
+ url: parsed.url ?? "",
94
+ title: parsed.title ?? "",
95
+ accessibilityTree: parsed.accessibilityTree ?? parsed.elements ?? [],
96
+ domSnapshot: parsed.domSnapshot ?? "",
97
+ };
98
+ }
99
+ // ─── Real browser scanner (Phase 1b) ───────────────────────────────────────
100
+ async function scanPageWithBrowser(opts) {
101
+ // Dynamic import — `playwright` is an optional dependency. If the user
102
+ // hasn't installed it, surface an actionable error rather than a stack trace.
103
+ let chromium;
104
+ try {
105
+ ({ chromium } = await Promise.resolve().then(() => __importStar(require("playwright"))));
106
+ }
107
+ catch (err) {
108
+ throw new McpError("Page discovery requires `playwright`. Install it in your project " +
109
+ "(`npm install -D playwright`) and run `npx playwright install chromium`. " +
110
+ "Alternatively, pre-capture a snapshot.json and pass --snapshot-file.", err);
111
+ }
112
+ let browser;
113
+ try {
114
+ browser = await chromium.launch({ headless: !opts.headed });
115
+ }
116
+ catch (err) {
117
+ throw new McpError("Could not launch chromium. Did you run `npx playwright install chromium`?", err);
118
+ }
119
+ try {
120
+ const ctx = await browser.newContext(opts.storageState ? { storageState: opts.storageState } : {});
121
+ const page = await ctx.newPage();
122
+ try {
123
+ await page.goto(opts.url, {
124
+ waitUntil: "networkidle",
125
+ timeout: opts.waitForNetworkIdleMs ?? 5000,
126
+ });
127
+ }
128
+ catch (err) {
129
+ // Network-idle is best-effort; some SPAs never reach it. Fall back to
130
+ // domcontentloaded — the page DOM is at least populated.
131
+ try {
132
+ await page.goto(opts.url, { waitUntil: "domcontentloaded", timeout: 10000 });
133
+ }
134
+ catch (innerErr) {
135
+ throw new McpError(`Could not navigate to ${opts.url}: ${innerErr.message}`, innerErr);
136
+ }
137
+ }
138
+ const title = await page.title().catch(() => "");
139
+ // Walk the DOM and extract every "interesting" element with a stable
140
+ // descriptor. The selector list covers anything a Gherkin step would
141
+ // plausibly target. Each descriptor maps 1:1 onto `ElementIR`, so
142
+ // `snapshotParser` (already in place) handles it without changes.
143
+ //
144
+ // The status/notification id+class patterns at the bottom catch elements
145
+ // like `<div id="error">` and `<div class="alert-warning">` that don't
146
+ // have an explicit ARIA role but are clearly named after their purpose.
147
+ // Without these, "Then I should see an error message" can't find a POM
148
+ // field and falls back to `page.getByText(...)` which often matches twice
149
+ // (real error + instructional copy on the page).
150
+ const elements = await page.evaluate(() => {
151
+ const SELECTORS = [
152
+ "input",
153
+ "textarea",
154
+ "select",
155
+ "button",
156
+ "a",
157
+ "[role]",
158
+ "[data-testid]",
159
+ "[aria-label]",
160
+ "h1",
161
+ "h2",
162
+ "h3",
163
+ "h4",
164
+ "h5",
165
+ "h6",
166
+ "label",
167
+ "[type=submit]",
168
+ // Status / notification regions — catch by id or class hint
169
+ "[id*=error i]",
170
+ "[id*=alert i]",
171
+ "[id*=message i]",
172
+ "[id*=warning i]",
173
+ "[id*=success i]",
174
+ "[id*=notification i]",
175
+ "[class*=error i]",
176
+ "[class*=alert i]",
177
+ "[class*=notification i]",
178
+ "[class*=banner i]",
179
+ ].join(",");
180
+ function getLabelFor(el) {
181
+ const id = el.getAttribute("id");
182
+ if (id) {
183
+ const lab = document.querySelector(`label[for="${CSS.escape(id)}"]`);
184
+ if (lab && lab.textContent)
185
+ return lab.textContent.trim();
186
+ }
187
+ const wrapping = el.closest("label");
188
+ if (wrapping && wrapping.textContent)
189
+ return wrapping.textContent.trim();
190
+ return undefined;
191
+ }
192
+ function bestCss(el) {
193
+ const id = el.getAttribute("id");
194
+ if (id)
195
+ return "#" + id;
196
+ const cls = typeof el.className === "string"
197
+ ? el.className.trim().split(/\s+/)[0]
198
+ : undefined;
199
+ if (cls)
200
+ return "." + cls;
201
+ return undefined;
202
+ }
203
+ /**
204
+ * Implicit ARIA role for native HTML elements. Without this, picker
205
+ * falls through `getByRole` (which requires a role) to `getByText` —
206
+ * which is brittle on pages where the same text appears in multiple
207
+ * places (button label vs instructional copy). Promoting `<button>`
208
+ * to `role: "button"` lets the picker emit
209
+ * page.getByRole("button", { name: "Submit" })
210
+ * which is what the user authoring tests by hand would write.
211
+ */
212
+ function implicitRole(el, tag) {
213
+ const explicit = el.getAttribute("role");
214
+ if (explicit)
215
+ return explicit;
216
+ if (tag === "button")
217
+ return "button";
218
+ if (tag === "a" && el.hasAttribute("href"))
219
+ return "link";
220
+ if (tag === "select")
221
+ return "combobox";
222
+ if (tag === "textarea")
223
+ return "textbox";
224
+ if (/^h[1-6]$/.test(tag))
225
+ return "heading";
226
+ if (tag === "input") {
227
+ const type = (el.getAttribute("type") || "text").toLowerCase();
228
+ if (type === "submit" || type === "button" || type === "reset")
229
+ return "button";
230
+ if (type === "checkbox")
231
+ return "checkbox";
232
+ if (type === "radio")
233
+ return "radio";
234
+ if (type === "search")
235
+ return "searchbox";
236
+ if (type === "range")
237
+ return "slider";
238
+ if (type === "number")
239
+ return "spinbutton";
240
+ // text, email, password, tel, url, etc. → textbox
241
+ return "textbox";
242
+ }
243
+ return undefined;
244
+ }
245
+ /**
246
+ * Filter out elements that are visually absent (display:none, hidden,
247
+ * or zero-sized). Decorative wrapper divs and screen-reader-only
248
+ * spans are not what test steps target.
249
+ *
250
+ * EXCEPTION: status regions (error / alert / notification banners)
251
+ * are *intended* to be hidden until triggered. Bypass the visibility
252
+ * check when the id or class hints at a status role, otherwise the
253
+ * `<div id="error">` element disappears from the POM and step
254
+ * matcher falls back to a non-unique getByText.
255
+ */
256
+ const STATUS_HINT = /error|alert|message|warning|success|notification|status/i;
257
+ function isLikelyStatusRegion(el) {
258
+ const id = el.getAttribute("id") || "";
259
+ const cls = typeof el.className === "string"
260
+ ? el.className
261
+ : "";
262
+ return STATUS_HINT.test(id) || STATUS_HINT.test(cls);
263
+ }
264
+ function isVisible(el) {
265
+ const r = el.getBoundingClientRect();
266
+ if (r.width === 0 || r.height === 0)
267
+ return false;
268
+ const cs = window.getComputedStyle(el);
269
+ if (cs.display === "none" || cs.visibility === "hidden" || cs.opacity === "0") {
270
+ return false;
271
+ }
272
+ return true;
273
+ }
274
+ const seen = new Set();
275
+ const out = [];
276
+ const all = document.querySelectorAll(SELECTORS);
277
+ all.forEach((el) => {
278
+ if (seen.has(el))
279
+ return;
280
+ seen.add(el);
281
+ // Status regions bypass the visibility filter
282
+ if (!isLikelyStatusRegion(el) && !isVisible(el))
283
+ return;
284
+ const tag = el.tagName.toLowerCase();
285
+ const role = implicitRole(el, tag);
286
+ const ariaLabel = el.getAttribute("aria-label") || undefined;
287
+ const placeholder = el.getAttribute("placeholder") || undefined;
288
+ const testId = el.getAttribute("data-testid") || undefined;
289
+ const text = (el.textContent || "").trim().replace(/\s+/g, " ").slice(0, 200);
290
+ // Accessible name — best-effort: aria-label > visible text (for
291
+ // buttons/links/headings) > placeholder.
292
+ let name = ariaLabel;
293
+ if (!name && /^(button|a|h[1-6]|label)$/.test(tag) && text)
294
+ name = text;
295
+ // For native form controls promoted to role:button via type=submit
296
+ if (!name && role === "button" && el.value) {
297
+ name = el.value;
298
+ }
299
+ out.push({
300
+ tag,
301
+ role,
302
+ name,
303
+ label: getLabelFor(el),
304
+ placeholder,
305
+ testId,
306
+ text: text || undefined,
307
+ cssSelector: bestCss(el),
308
+ });
309
+ });
310
+ return out;
311
+ });
312
+ const domSnapshot = await page
313
+ .evaluate(() => document.body.outerHTML.slice(0, 50000))
314
+ .catch(() => "");
315
+ return {
316
+ url: opts.url,
317
+ title,
318
+ accessibilityTree: elements,
319
+ domSnapshot,
320
+ };
321
+ }
322
+ finally {
323
+ await browser.close().catch(() => undefined);
324
+ }
325
+ }
326
+ //# sourceMappingURL=mcpClient.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mcpClient.js","sourceRoot":"","sources":["../../src/discovery/mcpClient.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,13 @@
1
+ /**
2
+ * Convert a `PageSnapshot` into a flat list of `ElementIR` candidates that
3
+ * the locator picker can rank.
4
+ *
5
+ * Accepts two snapshot shapes:
6
+ * 1) `accessibilityTree` is already an array of element-shaped objects
7
+ * (the file-snapshot fallback uses this — easy to author by hand)
8
+ * 2) `accessibilityTree` is a Playwright a11y tree node (with `children[]`)
9
+ * — flattened recursively.
10
+ */
11
+ import type { ElementIR } from "../types";
12
+ import type { PageSnapshot } from "./mcpClient";
13
+ export declare function parseSnapshot(snapshot: PageSnapshot): ElementIR[];
@@ -0,0 +1,51 @@
1
+ "use strict";
2
+ /**
3
+ * Convert a `PageSnapshot` into a flat list of `ElementIR` candidates that
4
+ * the locator picker can rank.
5
+ *
6
+ * Accepts two snapshot shapes:
7
+ * 1) `accessibilityTree` is already an array of element-shaped objects
8
+ * (the file-snapshot fallback uses this — easy to author by hand)
9
+ * 2) `accessibilityTree` is a Playwright a11y tree node (with `children[]`)
10
+ * — flattened recursively.
11
+ */
12
+ Object.defineProperty(exports, "__esModule", { value: true });
13
+ exports.parseSnapshot = parseSnapshot;
14
+ function parseSnapshot(snapshot) {
15
+ const tree = snapshot.accessibilityTree;
16
+ if (Array.isArray(tree)) {
17
+ return tree.map(toElementIR);
18
+ }
19
+ if (tree && typeof tree === "object") {
20
+ const out = [];
21
+ walk(tree, out);
22
+ return out;
23
+ }
24
+ return [];
25
+ }
26
+ function walk(node, out) {
27
+ if (!node)
28
+ return;
29
+ // Skip the synthetic root node if it has no role
30
+ if (node.role || node.name || node.tag) {
31
+ out.push(toElementIR(node));
32
+ }
33
+ for (const child of node.children ?? []) {
34
+ walk(child, out);
35
+ }
36
+ }
37
+ function toElementIR(raw) {
38
+ return {
39
+ role: raw.role || undefined,
40
+ name: raw.name || undefined,
41
+ label: raw.label || undefined,
42
+ placeholder: raw.placeholder || undefined,
43
+ testId: raw.testId || raw["data-testid"] || undefined,
44
+ text: raw.text || raw.innerText || undefined,
45
+ cssSelector: raw.cssSelector || raw.css || undefined,
46
+ xpath: raw.xpath || undefined,
47
+ tag: raw.tag || raw.tagName?.toLowerCase() || "",
48
+ bounds: raw.bounds,
49
+ };
50
+ }
51
+ //# sourceMappingURL=snapshotParser.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"snapshotParser.js","sourceRoot":"","sources":["../../src/discovery/snapshotParser.ts"],"names":[],"mappings":";AAAA;;;;;;;;;GASG;;AAKH,sCAWC;AAXD,SAAgB,aAAa,CAAC,QAAsB;IAClD,MAAM,IAAI,GAAG,QAAQ,CAAC,iBAAiB,CAAC;IACxC,IAAI,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC;QACxB,OAAO,IAAI,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC;IAC/B,CAAC;IACD,IAAI,IAAI,IAAI,OAAO,IAAI,KAAK,QAAQ,EAAE,CAAC;QACrC,MAAM,GAAG,GAAgB,EAAE,CAAC;QAC5B,IAAI,CAAC,IAAW,EAAE,GAAG,CAAC,CAAC;QACvB,OAAO,GAAG,CAAC;IACb,CAAC;IACD,OAAO,EAAE,CAAC;AACZ,CAAC;AAED,SAAS,IAAI,CAAC,IAAS,EAAE,GAAgB;IACvC,IAAI,CAAC,IAAI;QAAE,OAAO;IAClB,iDAAiD;IACjD,IAAI,IAAI,CAAC,IAAI,IAAI,IAAI,CAAC,IAAI,IAAI,IAAI,CAAC,GAAG,EAAE,CAAC;QACvC,GAAG,CAAC,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC,CAAC;IAC9B,CAAC;IACD,KAAK,MAAM,KAAK,IAAI,IAAI,CAAC,QAAQ,IAAI,EAAE,EAAE,CAAC;QACxC,IAAI,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC;IACnB,CAAC;AACH,CAAC;AAED,SAAS,WAAW,CAAC,GAAQ;IAC3B,OAAO;QACL,IAAI,EAAE,GAAG,CAAC,IAAI,IAAI,SAAS;QAC3B,IAAI,EAAE,GAAG,CAAC,IAAI,IAAI,SAAS;QAC3B,KAAK,EAAE,GAAG,CAAC,KAAK,IAAI,SAAS;QAC7B,WAAW,EAAE,GAAG,CAAC,WAAW,IAAI,SAAS;QACzC,MAAM,EAAE,GAAG,CAAC,MAAM,IAAI,GAAG,CAAC,aAAa,CAAC,IAAI,SAAS;QACrD,IAAI,EAAE,GAAG,CAAC,IAAI,IAAI,GAAG,CAAC,SAAS,IAAI,SAAS;QAC5C,WAAW,EAAE,GAAG,CAAC,WAAW,IAAI,GAAG,CAAC,GAAG,IAAI,SAAS;QACpD,KAAK,EAAE,GAAG,CAAC,KAAK,IAAI,SAAS;QAC7B,GAAG,EAAE,GAAG,CAAC,GAAG,IAAI,GAAG,CAAC,OAAO,EAAE,WAAW,EAAE,IAAI,EAAE;QAChD,MAAM,EAAE,GAAG,CAAC,MAAM;KACnB,CAAC;AACJ,CAAC"}