@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.
- package/CHANGELOG.md +308 -0
- package/LICENSE +21 -0
- package/README.md +254 -0
- package/dist/cli.d.ts +6 -0
- package/dist/cli.js +176 -0
- package/dist/cli.js.map +1 -0
- package/dist/discovery/mcpClient.d.ts +53 -0
- package/dist/discovery/mcpClient.js +326 -0
- package/dist/discovery/mcpClient.js.map +1 -0
- package/dist/discovery/snapshotParser.d.ts +13 -0
- package/dist/discovery/snapshotParser.js +51 -0
- package/dist/discovery/snapshotParser.js.map +1 -0
- package/dist/emitters/facade.d.ts +52 -0
- package/dist/emitters/facade.js +125 -0
- package/dist/emitters/facade.js.map +1 -0
- package/dist/http/artifacts.d.ts +14 -0
- package/dist/http/artifacts.js +36 -0
- package/dist/http/artifacts.js.map +1 -0
- package/dist/http/jobs.d.ts +26 -0
- package/dist/http/jobs.js +59 -0
- package/dist/http/jobs.js.map +1 -0
- package/dist/http/routes.d.ts +11 -0
- package/dist/http/routes.js +267 -0
- package/dist/http/routes.js.map +1 -0
- package/dist/http/schemas.d.ts +167 -0
- package/dist/http/schemas.js +57 -0
- package/dist/http/schemas.js.map +1 -0
- package/dist/index.d.ts +49 -0
- package/dist/index.js +507 -0
- package/dist/index.js.map +1 -0
- package/dist/llm/governanceClient.d.ts +24 -0
- package/dist/llm/governanceClient.js +22 -0
- package/dist/llm/governanceClient.js.map +1 -0
- package/dist/llm/providers/anthropic.d.ts +8 -0
- package/dist/llm/providers/anthropic.js +12 -0
- package/dist/llm/providers/anthropic.js.map +1 -0
- package/dist/llm/providers/gemini.d.ts +5 -0
- package/dist/llm/providers/gemini.js +9 -0
- package/dist/llm/providers/gemini.js.map +1 -0
- package/dist/llm/providers/openai.d.ts +5 -0
- package/dist/llm/providers/openai.js +9 -0
- package/dist/llm/providers/openai.js.map +1 -0
- package/dist/parser/gherkinParser.d.ts +17 -0
- package/dist/parser/gherkinParser.js +143 -0
- package/dist/parser/gherkinParser.js.map +1 -0
- package/dist/repo/projectScaffolder.d.ts +24 -0
- package/dist/repo/projectScaffolder.js +64 -0
- package/dist/repo/projectScaffolder.js.map +1 -0
- package/dist/repo/repoScanner.d.ts +21 -0
- package/dist/repo/repoScanner.js +132 -0
- package/dist/repo/repoScanner.js.map +1 -0
- package/dist/reports/reviewReport.d.ts +19 -0
- package/dist/reports/reviewReport.js +96 -0
- package/dist/reports/reviewReport.js.map +1 -0
- package/dist/server.d.ts +10 -0
- package/dist/server.js +40 -0
- package/dist/server.js.map +1 -0
- package/dist/transformers/locatorPicker.d.ts +40 -0
- package/dist/transformers/locatorPicker.js +283 -0
- package/dist/transformers/locatorPicker.js.map +1 -0
- package/dist/transformers/pomResolver.d.ts +28 -0
- package/dist/transformers/pomResolver.js +35 -0
- package/dist/transformers/pomResolver.js.map +1 -0
- package/dist/transformers/stepMatcher.d.ts +36 -0
- package/dist/transformers/stepMatcher.js +461 -0
- package/dist/transformers/stepMatcher.js.map +1 -0
- package/dist/types.d.ts +166 -0
- package/dist/types.js +7 -0
- package/dist/types.js.map +1 -0
- package/dist/utils/logger.d.ts +6 -0
- package/dist/utils/logger.js +25 -0
- package/dist/utils/logger.js.map +1 -0
- package/dist/utils/naming.d.ts +23 -0
- package/dist/utils/naming.js +58 -0
- package/dist/utils/naming.js.map +1 -0
- package/dist/validate/tscRunner.d.ts +17 -0
- package/dist/validate/tscRunner.js +98 -0
- package/dist/validate/tscRunner.js.map +1 -0
- package/package.json +69 -0
- package/templates/gitignore.tmpl +10 -0
- package/templates/package.json.tmpl +16 -0
- package/templates/playwright.config.ts.tmpl +24 -0
- 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
|
package/dist/cli.js.map
ADDED
|
@@ -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"}
|