@vijaypjavvadi/bdd2pw 3.8.1 → 3.10.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/README.md CHANGED
@@ -1,332 +1,396 @@
1
- # bdd2pw — Gherkin `.feature` → Playwright TypeScript scaffold
2
-
3
- [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)
4
- [![Node](https://img.shields.io/badge/node-%E2%89%A518-brightgreen?logo=node.js)](https://nodejs.org/)
5
- [![Tests](https://img.shields.io/badge/tests-101%20passing-brightgreen)](./tests)
6
- [![Real-world fixtures](https://img.shields.io/badge/real--world%20fixtures-2-brightgreen)](./examples)
7
-
8
- Most teams writing BDD test cases (via Cucumber `.feature` files, possibly authored by an LLM) face the same gap: the `.feature` describes the test, but **someone still has to write the Page Objects and the Playwright spec by hand**. That's the work `bdd2pw` does for you.
9
-
10
- Point it at a `.feature` file and a URL. It parses the Gherkin, scans the live page with Chromium, picks the most stable locator for every interactive element, matches each step to a POM method call, and emits a runnable Playwright TypeScript repo — including `playwright.config.ts`, `pages/login.page.ts`, `tests/login.spec.ts`, and a `BDD_REVIEW.md` listing anything a human still needs to look at.
11
-
12
- ## What `bdd2pw` is and isn't
13
-
14
- `bdd2pw` is a **scaffolder**, not an oracle. The output is honest about its limits:
15
-
16
- - **Specific `.feature` files** — quoted credentials, concrete assertion targets, standard verb forms — convert to **runnable specs with zero hand-edits**. Validated end-to-end against a real public site (`https://practicetestautomation.com/practice-test-login/`): 7/7 scenarios green.
17
- - **Vague `.feature` files** — "Enter valid username and password and click login button" with no quoted values — get partial coverage. Steps that can't be unambiguously mapped land as `// TODO` comments in the spec, and warnings in `BDD_REVIEW.md`. The user finishes the 1-3 ambiguous steps; the locators, POM scaffold, project config, and Background `goto()` are already done.
18
- - **No LLM in the v1.0 hot path.** The 30-rule matcher (14 in v1.0, +1 URL-contains in v1.0.1, +7 LLM-narrative in v1.1.1, +4 in v1.1.2, +2 in v1.1.3 with cross-role synthesis + strict-mode-safe fallbacks) covers three Cucumber dialects (first-person, third-person, no-subject) plus compound input steps. Genuinely vague steps are inherently ambiguous and get `// TODO` — better than an LLM guessing.
19
- - **`updatePom` is append-only by construction.** Re-scanning a page that already has a Page Object adds new locators only. Hand-edited method bodies, custom helper methods, custom imports are all preserved byte-identical. Never deletes, never renames, never modifies.
20
-
21
- > **The pitch.** Run `bdd2pw scaffold` and you get a Playwright TS repo where `npx playwright test` runs against the real site. For specific fixtures, all green. For vague ones, the 60-90% you'd otherwise hand-write is done; you finish the rest.
22
-
23
- ## Where this fits in the platform
24
-
25
- | Stage | Service | Role |
26
- |---|---|---|
27
- | 1. Generate | `test-case-generation-service` (FastAPI :4100) | Author `.feature` files from requirements |
28
- | **1.5. Scaffold** | **`bdd2pw` (this repo) — Express :4300 + CLI** | `.feature` + URL → POM + spec, ready to run |
29
- | 2. Migrate | [`@vijaypjavvadi/sel2pw`](https://github.com/javvadivijayprasad/sel2pw) (Express :4200) | Legacy Selenium → Playwright |
30
- | 3. Stabilise | `self-healing-stage-services` (FastAPI :8003) | Heal locators at runtime |
31
- | 4. Govern | `ai-governance` (Python lib + sidecar :8004) | Sanitise every LLM payload (when v1.1 LLM fallback ships) |
32
- | Orchestrate | `modern-automation-platform` (Express :3000) | Auth, jobs, UI, artifacts |
33
-
34
- `bdd2pw` and `sel2pw` are siblings. Both consume **`@vijaypjavvadi/pw-emit`** (the shared emitter library), so the Page Objects produced by one are byte-identical in shape to those produced by the other.
35
-
36
- ## Install
37
-
38
- ```bash
39
- npm install -D @vijaypjavvadi/bdd2pw
40
- npx playwright install chromium # browser for live page discovery
41
- ```
42
-
43
- Node ≥ 18. Cross-platform (Linux / macOS / Windows). `playwright` is an optional dep — if you don't install browsers, pass `--snapshot-file <path.json>` instead.
44
-
45
- ## Quick start
46
-
47
- ```bash
48
- # Scaffold a runnable Playwright TS project from a .feature file
49
- npx bdd2pw scaffold path/to/login.feature \
50
- --url https://your-app.example.com/login \
51
- --page LoginPage \
52
- --repo ./my-tests
53
-
54
- cd ./my-tests
55
- npm install
56
- npx playwright test
57
- ```
58
-
59
- That's it. `./my-tests` now has `pages/login.page.ts`, `tests/login.spec.ts`, `playwright.config.ts`, `package.json`, `tsconfig.json`, `.gitignore`, and `BDD_REVIEW.md`.
60
-
61
- ## LLM fallback (v2.0+)
62
-
63
- When a Gherkin step doesn't match any of bdd2pw's 30 deterministic rules, you can opt-in to an Anthropic-backed LLM fallback that produces the binding instead of dropping to `// TODO`. Off by default.
64
-
65
- ```bash
66
- # Anthropic API key — required when --llm anthropic is set
67
- export ANTHROPIC_API_KEY=sk-ant-api03-...
68
-
69
- bdd2pw scaffold ./my.feature \
70
- --url https://app.example.com \
71
- --page LoginPage \
72
- --repo ./out \
73
- --llm anthropic \
74
- --governance-url http://localhost:4900
75
- ```
76
-
77
- Every successful LLM-binding is appended to `<repo>/artefacts/candidate-rules.jsonl` so an offline review pipeline can propose new deterministic rules. Auto-write back to `stepMatcher.ts` is deferred to v2.1+ — for v2.0 the LLM is a runtime overlay, never a code generator for the matcher itself.
78
-
79
- ### Governance sanitization (mandatory by default)
80
-
81
- Per the platform contract, **every prompt passes through the `ai-governance` sidecar's `/sanitize` endpoint** before reaching Anthropic. This scrubs API keys, JWTs, AWS creds, and other secrets that might leak via test data in the `.feature` file. **Fail-closed** — if the sidecar is unreachable, the LLM call is REFUSED and the step falls back to TODO.
82
-
83
- Two ways to satisfy this:
84
-
85
- **(a) Internal platform users:** the `ai-governance` service is part of your platform service mesh. Just point bdd2pw at it (`--governance-url http://ai-governance:4900` or wherever).
86
-
87
- **(b) External users:** run your own sidecar.
88
-
89
- ```bash
90
- # Clone and run the sidecar
91
- git clone https://github.com/javvadivijayprasad/ai-governance
92
- cd ai-governance
93
- python -m venv .venv && .\.venv\Scripts\Activate.ps1 # or `source .venv/bin/activate` on Mac/Linux
94
- pip install fastapi uvicorn pydantic pyyaml
95
- PYTHONPATH=src uvicorn service.app:app --port 4900
96
- ```
97
-
98
- Or there's a Docker image once it's published: `docker run -p 4900:4900 ghcr.io/javvadivijayprasad/ai-governance:latest`.
99
-
100
- **Test-only escape hatch:** `--llm-skip-governance` bypasses the sidecar entirely. Use only when your `.feature` data is synthetic (no real credentials, no PII). For production, **always** keep governance on.
101
-
102
- ```bash
103
- # UNSAFE for real test data — use only with synthetic fixtures
104
- bdd2pw scaffold ./my.feature ... --llm anthropic --llm-skip-governance
105
- ```
106
-
107
- ### Cost + determinism guardrails
108
-
109
- | Flag | Default | Purpose |
110
- |---|---|---|
111
- | `--llm-max-calls <n>` | `50` | Hard cap on provider calls per scaffold. Cache hits don't count. |
112
- | `--llm-cache <path>` | `<repo>/.bdd2pw/llm-cache.sqlite` | SQLite cache keyed by step text + POM signature + model. Same inputs across runs return the same binding — cost goes to zero on re-runs. Pass `:memory:` for one-shot use. |
113
- | `--llm-model <model>` | `claude-sonnet-4-6` | Override the Anthropic model. |
114
-
115
- Re-runs of the same `.feature` (after a code edit, retry, etc.) will hit the cache and make zero provider calls. A typical per-scaffold cost on first run: ~$0.005–0.02 USD with Sonnet, depending on how many steps need LLM fallback.
116
-
117
- ### Cloud-jobs deployment recipe
118
-
119
- If you're running bdd2pw in an automation pipeline (cloud-jobs-template, GitHub Actions, etc.):
120
-
121
- ```yaml
122
- env:
123
- ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }} # platform secrets store
124
-
125
- steps:
126
- - name: Run scaffold with LLM fallback
127
- run: |
128
- bdd2pw scaffold "$FEATURE_FILE" \
129
- --url "$TARGET_URL" \
130
- --page "$PAGE_NAME" \
131
- --repo /work \
132
- --self-healing \
133
- --llm anthropic \
134
- --governance-url http://ai-governance:4900
135
- ```
136
-
137
- Make sure the `ai-governance` sidecar is co-located (k8s sidecar pattern) or reachable on the platform service mesh.
138
-
139
- ## CLI surface — three commands
140
-
141
- ### `scaffold` — generate a fresh repo
142
-
143
- ```bash
144
- bdd2pw scaffold <feature> --url <url> --page <Name> --repo <dir> [options]
145
- ```
146
-
147
- Reads the `.feature`, scans the URL with Chromium, picks locators, matches steps, emits the project. Flags:
148
-
149
- ```
150
- --snapshot-file <path> Use a captured JSON snapshot instead of launching a browser
151
- --no-discovery Skip page discovery entirely (rule-only probing)
152
- --storage-state <path> Pre-authenticated storage state JSON
153
- --headed Show browser during scan
154
- --templates <dir> Override default project templates
155
- --dry-run Print plan, write nothing
156
- --no-validate Skip tsc --noEmit step
157
- --force Overwrite existing spec files
158
- ```
159
-
160
- ### `analyze` — dry-run the matcher
161
-
162
- ```bash
163
- bdd2pw analyze <feature> --url <url> [options]
164
- ```
165
-
166
- Same pipeline but writes nothing — prints the parsed feature, the discovered locators, and the planned step bindings as JSON. Useful for "what would scaffold do?" probing.
167
-
168
- ### `update-pom` — merge new locators into an existing POM
169
-
170
- ```bash
171
- bdd2pw update-pom --page <Name> --url <url> --repo <dir> [options]
172
- ```
173
-
174
- Re-scan the page and **append** any newly-discovered locators to the existing Page Object. Never touches existing fields, methods, or specs. Hand-edited method bodies survive byte-identical.
175
-
176
- ## HTTP service
177
-
178
- `bdd2pw serve` starts an Express service on `:4300` with the same three commands as REST endpoints. Mirror of the CLI; HTTP is a thin shell.
179
-
180
- ```bash
181
- bdd2pw serve --port 4300
182
-
183
- # In another terminal:
184
- curl -X POST http://localhost:4300/scaffold \
185
- -H 'content-type: application/json' \
186
- -d '{"feature":"/abs/path/to/login.feature","url":"https://...","page":"LoginPage","repo":"unused","options":{"snapshotFile":"/abs/snapshot.json","noValidate":true}}'
187
- # → 202 Accepted: {"jobId":"01HX...","links":{"self":"/jobs/01HX...","artifact":"/jobs/01HX.../artifact"}}
188
-
189
- curl http://localhost:4300/jobs/01HX... # poll for completion
190
- curl -O http://localhost:4300/jobs/01HX.../artifact # download zip
191
- ```
192
-
193
- | Endpoint | Purpose |
194
- |---|---|
195
- | `GET /healthz`, `/version`, `/readyz` | Liveness probes |
196
- | `POST /scaffold`, `/analyze`, `/update-pom` | Async job — returns `202` + `jobId` |
197
- | `GET /jobs/:id` | Job status / progress / result |
198
- | `GET /jobs/:id/artifact` | Stream zip of the scaffolded repo |
199
- | `GET /jobs/:id/log` | Plain-text dump of warnings + errors |
200
-
201
- In production, the platform gateway proxies `/api/v1/scaffold/*` to `:4300`. Direct port exposure is local-dev only.
202
-
203
- ## Pipeline
204
-
205
- ```
206
- .feature file
207
- │
208
- ▼
209
- ┌──────────────┐
210
- │ gherkinParser│ @cucumber/gherkin → FeatureIR (scenarios, background, examples)
211
- └──────┬───────┘
212
- ▼
213
- ┌──────────────┐
214
- │ repoScanner │ ts-morph → existing pages/*.page.ts → Map<class, IR>
215
- └──────┬───────┘
216
- ▼
217
- ┌──────────────┐
218
- │ pomResolver │ CREATE / AUGMENT / REUSE decision
219
- └──────┬───────┘
220
- ▼
221
- ┌──────────────┐
222
- │ scanPage │ playwright chromium → DOM walk → ElementIR[]
223
- │ │ (or --snapshot-file / --no-discovery fallbacks)
224
- └──────┬───────┘
225
- ▼
226
- ┌──────────────┐
227
- │ locatorPicker│ getByRole > getByLabel > getByPlaceholder > getByTestId
228
- │ │ > getByText > css > xpath
229
- │ │ Status-region special case + dedup by (api, args)
230
- └──────┬───────┘
231
- ▼
232
- ┌──────────────┐
233
- │ stepMatcher │ 30 rules — subject-agnostic, dialect-tolerant,
234
- │ │ compound-input via customBody
235
- └──────┬───────┘
236
- ▼
237
- ┌──────────────┐
238
- │ pw-emit │ @vijaypjavvadi/pw-emit → POM TS + spec TS + project scaffold
239
- └──────┬───────┘
240
- ▼
241
- ┌──────────────┐
242
- │ tscRunner │ Optional tsc --noEmit → diagnostics into BDD_REVIEW.md
243
- └──────┬───────┘
244
- ▼
245
- output/
246
- pages/<name>.page.ts
247
- tests/<name>.spec.ts
248
- playwright.config.ts
249
- package.json, tsconfig.json, .gitignore
250
- BDD_REVIEW.md ← warnings + manual TODOs
251
- ```
252
-
253
- ## What `bdd2pw` covers
254
-
255
- ### Step matcher rules (14)
256
-
257
- | # | Pattern | Emits |
258
- |---|---|---|
259
- | 1 | `(I/user) am on/navigate to/visit/open <X>` (with or without quoted URL) | `goto()` |
260
- | 2a | `(I/user) enter "<value>" into <field>` | `<field>.fill("<value>")` |
261
- | 2b | `(I/user) enter <field> "<value>"` | `<field>.fill("<value>")` |
262
- | 2c | **Compound:** `(I/user) enter <f1> "<v1>" <f2> "<v2>" ...` | N `.fill()` lines |
263
- | 3 | `(I/user/-) click/press/tap <X> button/link/icon/tab` | `<field>.click()` (or POM method if defined) |
264
- | 4 | `(I/user) select "<opt>" from <dropdown>` | `<field>.selectOption(...)` |
265
- | 5 | `(I/user) check/uncheck <checkbox>` | `<field>.check()` / `.uncheck()` |
266
- | 6 | `(I/user) (should) see <X>` | `expect(<X>).toBeVisible()` |
267
- | 7 | `(I/user) should not see <X>` | `expect(<X>).toBeHidden()` |
268
- | 8 | `(I/user) should see (the/an/a) error message "<X>"` | `expect(<errorField>).toContainText("<X>")` |
269
- | 9a | `(I/user) should see <X> containing/with text "<Y>"` | `expect(<X>).toContainText("<Y>")` |
270
- | 9b | `(I/user) should see <X> "<Y>"` (exact equality) | `expect(<X>).toHaveText("<Y>")` |
271
- | 10 | `(I/user) should remain on <page>` | `expect(page).toHaveURL(/<page>/)` |
272
- | 11 | `(I/user) (should be/is) redirected to <page>` | `expect(page).toHaveURL(/<page>/)` |
273
- | 12 | `the <field> field should be of type "<type>"` | `expect(<field>).toHaveAttribute("type", "<type>")` |
274
- | 13 | `the (current) URL should start with/contain "<X>"` | `expect(page).toHaveURL(/^<X>/)` |
275
-
276
- ### Cucumber features
277
-
278
- - `Background:` → `test.beforeEach`
279
- - `Scenario Outline:` + `Examples:` → one `test()` per row, with placeholder substitution
280
- - `Tags` → emitted as `// @tag` comments above each test
281
- - Doc strings + data tables — preserved on `StepIR.argument`
282
-
283
- ### Locator priority
284
-
285
- `getByRole` (with accessible name) > `getByLabel` > `getByPlaceholder` > `getByTestId` > `getByText` > CSS > xpath. **Status regions** (`<div id="error">`, `[role=alert]`, `[class*=notification]`) bypass the visibility filter (they're hidden until triggered) and use their `id` for both field name and locator (text content is volatile, id is stable).
286
-
287
- ## Verification
288
-
289
- | Check | Status |
290
- |---|---|
291
- | `npm run lint` | ✅ clean |
292
- | `tsc --noEmit` | ✅ clean |
293
- | `npm test` (vitest) | ✅ **101/101 green** across 10 test files |
294
- | `practice-test-login` end-to-end vs the live site | ✅ **7/7** scenarios green via `npx playwright test` |
295
- | `cms-login` (selenium14 dialect) regression | ✅ 12 assertions green, third-person + compound input + `is redirected` |
296
- | HTTP worker zip download | ✅ end-to-end test verifies real ZIP magic bytes |
297
- | `update-pom` preserves hand-edited method bodies | ✅ 5 byte-identical-survival assertions |
298
-
299
- ## Roadmap
300
-
301
- | Phase | Status | Headline |
302
- |---|---|---|
303
- | Phase 0 | ✅ Shipped | `@vijaypjavvadi/pw-emit` extracted; sibling package consumed by both `bdd2pw` and (planned) `sel2pw v1.2` |
304
- | Phase 1a | ✅ Shipped | Gherkin parser, step matcher rules, locator picker, POM resolver, repo scanner, file-snapshot discovery, scaffold orchestration |
305
- | Phase 1b | ✅ Shipped | Real Chromium-based page discovery via `playwright`, dialect-agnostic rules |
306
- | Phase 1c | ✅ Shipped | Third-person + compound-input dialect coverage; second real-world fixture pinned |
307
- | Phase 2 | ✅ Shipped | `update-pom` with append-only AST surgery — preserves hand-edits byte-identical |
308
- | Phase 3 | ✅ Shipped | HTTP service `:4300` with three real workers + zip artifact download |
309
- | **v1.0 (this release)** | **Ready to tag** | All of the above |
310
- | v1.1 | Roadmap | LLM fallback for genuinely-vague steps, governance-routed |
311
- | v1.2 | Roadmap | sel2pw migration onto `pw-emit`; multi-page scenario discovery |
312
- | v1.3 | Roadmap | Optional `playwright-bdd` runtime mode |
313
-
314
- ## Documents
315
-
316
- | Doc | Purpose |
317
- |---|---|
318
- | [`docs/SCOPE.md`](./docs/SCOPE.md) | What's in / out of v1.0, FR/NFR, CLI + HTTP surface, decisions log |
319
- | [`docs/ARCHITECTURE.md`](./docs/ARCHITECTURE.md) | Component diagram, sequence diagrams, `pw-emit` contract, HTTP API contract, error model |
320
- | [`docs/PHASE_0_PLAN.md`](./docs/PHASE_0_PLAN.md) | The pw-emit extraction plan (mostly historical now — it shipped) |
321
- | [`docs/STATUS.md`](./docs/STATUS.md) | Verified state right now — what works, what's pending, what's deferred |
322
- | [`CHANGELOG.md`](./CHANGELOG.md) | Full version history |
323
- | [`CONTRIBUTING.md`](./CONTRIBUTING.md) | Local dev, branching, PR conventions |
324
-
325
- ## Examples
326
-
327
- - [`examples/practice-test-login/`](./examples/practice-test-login/) — first-person dialect, `Scenario Outline + Examples`, security scenarios. Validated 7/7 green against a live public site.
328
- - [`examples/cms-login/`](./examples/cms-login/) — third-person dialect, compound input, `is redirected`. From a real OSS Selenium codebase.
329
-
330
- ## License
331
-
332
- MIT
1
+ [![DOI](https://zenodo.org/badge/DOI/10.5281/zenodo.20450278.svg)](https://doi.org/10.5281/zenodo.20450278)
2
+
3
+
4
+ # bdd2pw — Gherkin `.feature` → Playwright TypeScript scaffold
5
+
6
+ # @vijaypjavvadi/bdd2pw
7
+
8
+ [![npm version](https://img.shields.io/npm/v/@vijaypjavvadi/bdd2pw.svg)](https://www.npmjs.com/package/@vijaypjavvadi/bdd2pw)
9
+ [![npm downloads](https://img.shields.io/npm/dm/@vijaypjavvadi/bdd2pw.svg)](https://www.npmjs.com/package/@vijaypjavvadi/bdd2pw)
10
+ [![npm total downloads](https://img.shields.io/npm/dt/@vijaypjavvadi/bdd2pw.svg)](https://www.npmjs.com/package/@vijaypjavvadi/bdd2pw)
11
+ [![license](https://img.shields.io/npm/l/@vijaypjavvadi/bdd2pw.svg)](https://github.com/javvadivijayprasad/bdd2pw/blob/main/LICENSE)
12
+ [![Listed in Awesome Playwright](https://awesome.re/mentioned-badge.svg)](https://github.com/mxschmitt/awesome-playwright)
13
+
14
+ > Scaffold runnable Playwright TypeScript tests from Gherkin `.feature` files. Auto-detects existing Page Objects, scans live pages via Microsoft Playwright MCP, emits POMs and specs ready for execution. CLI + HTTP service.
15
+
16
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)
17
+ [![Node](https://img.shields.io/badge/node-%E2%89%A518-brightgreen?logo=node.js)](https://nodejs.org/)
18
+ [![npm](https://img.shields.io/npm/v/@vijaypjavvadi/bdd2pw.svg?label=npm)](https://www.npmjs.com/package/@vijaypjavvadi/bdd2pw)
19
+ [![Tests](https://img.shields.io/badge/tests-290%2B%20passing-brightgreen)](./tests)
20
+ [![Rules](https://img.shields.io/badge/rules-200%2B%20deterministic-brightgreen)](./src/transformers)
21
+ [![Domain packs](https://img.shields.io/badge/domain%20packs-7-blue)](./src/transformers/domains)
22
+ [![VS Code Marketplace](https://img.shields.io/visual-studio-marketplace/v/vijaypjavvadi.bdd2pw?label=vscode-extension)](https://marketplace.visualstudio.com/items?itemName=vijaypjavvadi.bdd2pw)
23
+
24
+ Most teams writing BDD test cases (via Cucumber `.feature` files, possibly authored by an LLM) face the same gap: the `.feature` describes the test, but **someone still has to write the Page Objects and the Playwright spec by hand**. That's the work `bdd2pw` does for you.
25
+
26
+ Point it at a `.feature` file and a URL. It parses the Gherkin, scans the live page with Chromium, picks the most stable locator for every interactive element, matches each step to a POM method call, and emits a runnable Playwright TypeScript repo — including `playwright.config.ts`, `pages/login.page.ts`, `tests/login.spec.ts`, and a `BDD_REVIEW.md` listing anything a human still needs to look at.
27
+
28
+ ## What `bdd2pw` is and isn't (as of v3.8.1)
29
+
30
+ `bdd2pw` is a **deterministic scaffolder with an optional governed LLM fallback**. The output is honest about its limits:
31
+
32
+ - **Specific `.feature` files** — quoted credentials, concrete assertion targets, standard verb forms — convert to **runnable specs with zero hand-edits**. Validated end-to-end against real public sites (`practicetestautomation.com`, `owasp-juice-shop`): every scenario green.
33
+ - **Vague `.feature` files** — "Enter valid username and password and click login button" with no quoted values — get partial coverage. Unmatched steps either route to the LLM fallback (when an Anthropic / OpenAI key is configured) or land as `// TODO` comments + warnings in `BDD_REVIEW.md`.
34
+ - **200+ deterministic rules**, organised across three layers:
35
+ - **API rules** (v3.0+) — `page.request.*` patterns for GET / POST / PUT / DELETE / PATCH, body / header / status assertions.
36
+ - **Visibility rules** (v3.1+) — `<noun> is visible / displayed / shown / appears` intercepts before URL-slug heuristics, so prose like "the user's name or profile indicator is visible in the UI" never gets slugified into a URL regex.
37
+ - **UI + URL rules** (v1.x lineage, refined through v3.7.1) — 30+ rules covering first-person, third-person, no-subject Cucumber dialects + compound input steps.
38
+ - **7 opt-in domain rule packs** (v3.4 + v3.8) — banking, healthcare, insurance, retail, gov, education, telecom. Each adds ~20 industry-specific patterns. Activated via `domains: ["banking"]`; empty by default for byte-stable existing behaviour.
39
+ - **LLM fallback is batched per scenario** (v3.5) — one Anthropic / OpenAI / Gemini call per scenario instead of one per unmatched step. ~75% cost reduction on unmatched-heavy scaffolds. Every prompt goes through the `ai-governance` sidecar's `/sanitize` endpoint (fail-closed).
40
+ - **`updatePom` is append-only by construction.** Re-scanning a page that already has a Page Object adds new locators only. Hand-edited method bodies, custom helper methods, custom imports are all preserved byte-identical.
41
+ - **`--merge` mode** (v3.2) preserves `// bdd2pw:user-block id="..."` sections across regenerations so iterative locator tuning doesn't lose work.
42
+
43
+ > **The pitch.** Run `bdd2pw scaffold` and you get a Playwright TS repo where `npx playwright test` runs against the real site. For specific fixtures, all green. For vague ones, the 60-90% you'd otherwise hand-write is done; you finish the rest — or you let the governed LLM finish them.
44
+
45
+ ## What's new in v3.9.0
46
+
47
+ - **NEW** `llmStats: true` writes `<repo>/artefacts/llm-stats.json` with per-call latency, token counts, cache hit rate, and an estimated cost in USD. Finally makes the v3.5 batching ROI measurable per run.
48
+ - Seven domain rule packs (banking, healthcare, insurance, retail, gov, education, telecom) — ~140 industry-specific patterns total.
49
+ - Per-scenario LLM batching cuts Anthropic spend by ~75% on unmatched-heavy scaffolds.
50
+ - `bdd2pw propose-rules` CLI clusters past LLM bindings into draft regex proposals — turning the offline-review pipeline into one command.
51
+ - `diagnostics: true` adds a "Rule trace" block to `BDD_REVIEW.md` showing exactly which rules declined a step and why.
52
+ - `merge: true` preserves user-edited blocks across regenerations.
53
+ - `metaSidecar: true` writes `<spec>.spec.meta.json` describing every step's semantic intent for downstream tools.
54
+ - VS Code extension (`vijaypjavvadi.bdd2pw`) v0.2.0 surfaces all the above as settings + commands.
55
+
56
+ ## Where this fits in the platform
57
+
58
+ | Stage | Service | Role |
59
+ |---|---|---|
60
+ | 1. Generate | `test-case-generation-service` (FastAPI :4100) | Author `.feature` files from requirements |
61
+ | **1.5. Scaffold** | **`bdd2pw` (this repo) — Express :4300 + CLI** | `.feature` + URL → POM + spec, ready to run |
62
+ | 2. Migrate | [`@vijaypjavvadi/sel2pw`](https://github.com/javvadivijayprasad/sel2pw) (Express :4200) | Legacy Selenium → Playwright |
63
+ | 3. Stabilise | `self-healing-stage-services` (FastAPI :8003) | Heal locators at runtime |
64
+ | 4. Govern | `ai-governance` (Python lib + sidecar :8004) | Sanitise every LLM payload (when v1.1 LLM fallback ships) |
65
+ | Orchestrate | `modern-automation-platform` (Express :3000) | Auth, jobs, UI, artifacts |
66
+
67
+ `bdd2pw` and `sel2pw` are siblings. Both consume **`@vijaypjavvadi/pw-emit`** (the shared emitter library), so the Page Objects produced by one are byte-identical in shape to those produced by the other.
68
+
69
+ ## Install
70
+
71
+ ```bash
72
+ npm install -D @vijaypjavvadi/bdd2pw
73
+ npx playwright install chromium # browser for live page discovery
74
+ ```
75
+
76
+ Node ≥ 18. Cross-platform (Linux / macOS / Windows). `playwright` is an optional dep — if you don't install browsers, pass `--snapshot-file <path.json>` instead.
77
+
78
+ ## Quick start
79
+
80
+ ```bash
81
+ # Scaffold a runnable Playwright TS project from a .feature file
82
+ npx bdd2pw scaffold path/to/login.feature \
83
+ --url https://your-app.example.com/login \
84
+ --page LoginPage \
85
+ --repo ./my-tests
86
+
87
+ cd ./my-tests
88
+ npm install
89
+ npx playwright test
90
+ ```
91
+
92
+ That's it. `./my-tests` now has `pages/login.page.ts`, `tests/login.spec.ts`, `playwright.config.ts`, `package.json`, `tsconfig.json`, `.gitignore`, and `BDD_REVIEW.md`.
93
+
94
+ ## LLM fallback (v2.0+)
95
+
96
+ When a Gherkin step doesn't match any of bdd2pw's 30 deterministic rules, you can opt-in to an Anthropic-backed LLM fallback that produces the binding instead of dropping to `// TODO`. Off by default.
97
+
98
+ ```bash
99
+ # Anthropic API key — required when --llm anthropic is set
100
+ export ANTHROPIC_API_KEY=sk-ant-api03-...
101
+
102
+ bdd2pw scaffold ./my.feature \
103
+ --url https://app.example.com \
104
+ --page LoginPage \
105
+ --repo ./out \
106
+ --llm anthropic \
107
+ --governance-url http://localhost:4900
108
+ ```
109
+
110
+ Every successful LLM-binding is appended to `<repo>/artefacts/candidate-rules.jsonl` so an offline review pipeline can propose new deterministic rules. Auto-write back to `stepMatcher.ts` is deferred to v2.1+ — for v2.0 the LLM is a runtime overlay, never a code generator for the matcher itself.
111
+
112
+ ### Governance sanitization (mandatory by default)
113
+
114
+ Per the platform contract, **every prompt passes through the `ai-governance` sidecar's `/sanitize` endpoint** before reaching Anthropic. This scrubs API keys, JWTs, AWS creds, and other secrets that might leak via test data in the `.feature` file. **Fail-closed** — if the sidecar is unreachable, the LLM call is REFUSED and the step falls back to TODO.
115
+
116
+ Two ways to satisfy this:
117
+
118
+ **(a) Internal platform users:** the `ai-governance` service is part of your platform service mesh. Just point bdd2pw at it (`--governance-url http://ai-governance:4900` or wherever).
119
+
120
+ **(b) External users:** run your own sidecar.
121
+
122
+ ```bash
123
+ # Clone and run the sidecar
124
+ git clone https://github.com/javvadivijayprasad/ai-governance
125
+ cd ai-governance
126
+ python -m venv .venv && .\.venv\Scripts\Activate.ps1 # or `source .venv/bin/activate` on Mac/Linux
127
+ pip install fastapi uvicorn pydantic pyyaml
128
+ PYTHONPATH=src uvicorn service.app:app --port 4900
129
+ ```
130
+
131
+ Or there's a Docker image once it's published: `docker run -p 4900:4900 ghcr.io/javvadivijayprasad/ai-governance:latest`.
132
+
133
+ **Test-only escape hatch:** `--llm-skip-governance` bypasses the sidecar entirely. Use only when your `.feature` data is synthetic (no real credentials, no PII). For production, **always** keep governance on.
134
+
135
+ ```bash
136
+ # UNSAFE for real test data — use only with synthetic fixtures
137
+ bdd2pw scaffold ./my.feature ... --llm anthropic --llm-skip-governance
138
+ ```
139
+
140
+ ### Cost + determinism guardrails
141
+
142
+ | Flag | Default | Purpose |
143
+ |---|---|---|
144
+ | `--llm-max-calls <n>` | `50` | Hard cap on provider calls per scaffold. Cache hits don't count. |
145
+ | `--llm-cache <path>` | `<repo>/.bdd2pw/llm-cache.sqlite` | SQLite cache keyed by step text + POM signature + model. Same inputs across runs return the same binding — cost goes to zero on re-runs. Pass `:memory:` for one-shot use. |
146
+ | `--llm-model <model>` | `claude-sonnet-4-6` | Override the Anthropic model. |
147
+
148
+ Re-runs of the same `.feature` (after a code edit, retry, etc.) will hit the cache and make zero provider calls. A typical per-scaffold cost on first run: ~$0.005–0.02 USD with Sonnet, depending on how many steps need LLM fallback.
149
+
150
+ ### Cloud-jobs deployment recipe
151
+
152
+ If you're running bdd2pw in an automation pipeline (cloud-jobs-template, GitHub Actions, etc.):
153
+
154
+ ```yaml
155
+ env:
156
+ ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }} # platform secrets store
157
+
158
+ steps:
159
+ - name: Run scaffold with LLM fallback
160
+ run: |
161
+ bdd2pw scaffold "$FEATURE_FILE" \
162
+ --url "$TARGET_URL" \
163
+ --page "$PAGE_NAME" \
164
+ --repo /work \
165
+ --self-healing \
166
+ --llm anthropic \
167
+ --governance-url http://ai-governance:4900
168
+ ```
169
+
170
+ Make sure the `ai-governance` sidecar is co-located (k8s sidecar pattern) or reachable on the platform service mesh.
171
+
172
+ ## CLI surface — three commands
173
+
174
+ ### `scaffold` — generate a fresh repo
175
+
176
+ ```bash
177
+ bdd2pw scaffold <feature> --url <url> --page <Name> --repo <dir> [options]
178
+ ```
179
+
180
+ Reads the `.feature`, scans the URL with Chromium, picks locators, matches steps, emits the project. Flags:
181
+
182
+ ```
183
+ --snapshot-file <path> Use a captured JSON snapshot instead of launching a browser
184
+ --no-discovery Skip page discovery entirely (rule-only probing)
185
+ --storage-state <path> Pre-authenticated storage state JSON
186
+ --headed Show browser during scan
187
+ --templates <dir> Override default project templates
188
+ --dry-run Print plan, write nothing
189
+ --no-validate Skip tsc --noEmit step
190
+ --force Overwrite existing spec files
191
+ ```
192
+
193
+ ### `analyze` — dry-run the matcher
194
+
195
+ ```bash
196
+ bdd2pw analyze <feature> --url <url> [options]
197
+ ```
198
+
199
+ Same pipeline but writes nothing — prints the parsed feature, the discovered locators, and the planned step bindings as JSON. Useful for "what would scaffold do?" probing.
200
+
201
+ ### `update-pom` — merge new locators into an existing POM
202
+
203
+ ```bash
204
+ bdd2pw update-pom --page <Name> --url <url> --repo <dir> [options]
205
+ ```
206
+
207
+ Re-scan the page and **append** any newly-discovered locators to the existing Page Object. Never touches existing fields, methods, or specs. Hand-edited method bodies survive byte-identical.
208
+
209
+ ## HTTP service
210
+
211
+ `bdd2pw serve` starts an Express service on `:4300` with the same three commands as REST endpoints. Mirror of the CLI; HTTP is a thin shell.
212
+
213
+ ```bash
214
+ bdd2pw serve --port 4300
215
+
216
+ # In another terminal:
217
+ curl -X POST http://localhost:4300/scaffold \
218
+ -H 'content-type: application/json' \
219
+ -d '{"feature":"/abs/path/to/login.feature","url":"https://...","page":"LoginPage","repo":"unused","options":{"snapshotFile":"/abs/snapshot.json","noValidate":true}}'
220
+ # → 202 Accepted: {"jobId":"01HX...","links":{"self":"/jobs/01HX...","artifact":"/jobs/01HX.../artifact"}}
221
+
222
+ curl http://localhost:4300/jobs/01HX... # poll for completion
223
+ curl -O http://localhost:4300/jobs/01HX.../artifact # download zip
224
+ ```
225
+
226
+ | Endpoint | Purpose |
227
+ |---|---|
228
+ | `GET /healthz`, `/version`, `/readyz` | Liveness probes |
229
+ | `POST /scaffold`, `/analyze`, `/update-pom` | Async job — returns `202` + `jobId` |
230
+ | `GET /jobs/:id` | Job status / progress / result |
231
+ | `GET /jobs/:id/artifact` | Stream zip of the scaffolded repo |
232
+ | `GET /jobs/:id/log` | Plain-text dump of warnings + errors |
233
+
234
+ In production, the platform gateway proxies `/api/v1/scaffold/*` to `:4300`. Direct port exposure is local-dev only.
235
+
236
+ ## Pipeline
237
+
238
+ ```
239
+ .feature file
240
+ │
241
+ ▼
242
+ ┌──────────────┐
243
+ │ gherkinParser│ @cucumber/gherkin → FeatureIR (scenarios, background, examples)
244
+ └──────┬───────┘
245
+ ▼
246
+ ┌──────────────┐
247
+ │ repoScanner │ ts-morph → existing pages/*.page.ts → Map<class, IR>
248
+ └──────┬───────┘
249
+ ▼
250
+ ┌──────────────┐
251
+ │ pomResolver │ CREATE / AUGMENT / REUSE decision
252
+ └──────┬───────┘
253
+ ▼
254
+ ┌──────────────┐
255
+ │ scanPage │ playwright chromium → DOM walk → ElementIR[]
256
+ │ │ (or --snapshot-file / --no-discovery fallbacks)
257
+ └──────┬───────┘
258
+ ▼
259
+ ┌──────────────┐
260
+ │ locatorPicker│ getByRole > getByLabel > getByPlaceholder > getByTestId
261
+ │ │ > getByText > css > xpath
262
+ │ │ Status-region special case + dedup by (api, args)
263
+ └──────┬───────┘
264
+ ▼
265
+ ┌──────────────┐
266
+ │ stepMatcher │ 30 rules — subject-agnostic, dialect-tolerant,
267
+ │ │ compound-input via customBody
268
+ └──────┬───────┘
269
+ ▼
270
+ ┌──────────────┐
271
+ │ pw-emit │ @vijaypjavvadi/pw-emit → POM TS + spec TS + project scaffold
272
+ └──────┬───────┘
273
+ ▼
274
+ ┌──────────────┐
275
+ │ tscRunner │ Optional tsc --noEmit → diagnostics into BDD_REVIEW.md
276
+ └──────┬───────┘
277
+ ▼
278
+ output/
279
+ pages/<name>.page.ts
280
+ tests/<name>.spec.ts
281
+ playwright.config.ts
282
+ package.json, tsconfig.json, .gitignore
283
+ BDD_REVIEW.md ← warnings + manual TODOs
284
+ ```
285
+
286
+ ## What `bdd2pw` covers
287
+
288
+ ### Step matcher rules (14)
289
+
290
+ | # | Pattern | Emits |
291
+ |---|---|---|
292
+ | 1 | `(I/user) am on/navigate to/visit/open <X>` (with or without quoted URL) | `goto()` |
293
+ | 2a | `(I/user) enter "<value>" into <field>` | `<field>.fill("<value>")` |
294
+ | 2b | `(I/user) enter <field> "<value>"` | `<field>.fill("<value>")` |
295
+ | 2c | **Compound:** `(I/user) enter <f1> "<v1>" <f2> "<v2>" ...` | N `.fill()` lines |
296
+ | 3 | `(I/user/-) click/press/tap <X> button/link/icon/tab` | `<field>.click()` (or POM method if defined) |
297
+ | 4 | `(I/user) select "<opt>" from <dropdown>` | `<field>.selectOption(...)` |
298
+ | 5 | `(I/user) check/uncheck <checkbox>` | `<field>.check()` / `.uncheck()` |
299
+ | 6 | `(I/user) (should) see <X>` | `expect(<X>).toBeVisible()` |
300
+ | 7 | `(I/user) should not see <X>` | `expect(<X>).toBeHidden()` |
301
+ | 8 | `(I/user) should see (the/an/a) error message "<X>"` | `expect(<errorField>).toContainText("<X>")` |
302
+ | 9a | `(I/user) should see <X> containing/with text "<Y>"` | `expect(<X>).toContainText("<Y>")` |
303
+ | 9b | `(I/user) should see <X> "<Y>"` (exact equality) | `expect(<X>).toHaveText("<Y>")` |
304
+ | 10 | `(I/user) should remain on <page>` | `expect(page).toHaveURL(/<page>/)` |
305
+ | 11 | `(I/user) (should be/is) redirected to <page>` | `expect(page).toHaveURL(/<page>/)` |
306
+ | 12 | `the <field> field should be of type "<type>"` | `expect(<field>).toHaveAttribute("type", "<type>")` |
307
+ | 13 | `the (current) URL should start with/contain "<X>"` | `expect(page).toHaveURL(/^<X>/)` |
308
+
309
+ ### Cucumber features
310
+
311
+ - `Background:` → `test.beforeEach`
312
+ - `Scenario Outline:` + `Examples:` → one `test()` per row, with placeholder substitution
313
+ - `Tags` → emitted as `// @tag` comments above each test
314
+ - Doc strings + data tables — preserved on `StepIR.argument`
315
+
316
+ ### Locator priority
317
+
318
+ `getByRole` (with accessible name) > `getByLabel` > `getByPlaceholder` > `getByTestId` > `getByText` > CSS > xpath. **Status regions** (`<div id="error">`, `[role=alert]`, `[class*=notification]`) bypass the visibility filter (they're hidden until triggered) and use their `id` for both field name and locator (text content is volatile, id is stable).
319
+
320
+ ## Verification
321
+
322
+ | Check | Status |
323
+ |---|---|
324
+ | `npm run lint` | ✅ clean |
325
+ | `tsc --noEmit` | ✅ clean |
326
+ | `npm test` (vitest) | ✅ **101/101 green** across 10 test files |
327
+ | `practice-test-login` end-to-end vs the live site | ✅ **7/7** scenarios green via `npx playwright test` |
328
+ | `cms-login` (selenium14 dialect) regression | ✅ 12 assertions green, third-person + compound input + `is redirected` |
329
+ | HTTP worker zip download | ✅ end-to-end test verifies real ZIP magic bytes |
330
+ | `update-pom` preserves hand-edited method bodies | ✅ 5 byte-identical-survival assertions |
331
+
332
+ ## Roadmap
333
+
334
+ ### Shipped (Aug 2025 → May 2026)
335
+
336
+ | Version | Theme | Highlight |
337
+ |---|---|---|
338
+ | v1.0 | Foundation | Gherkin → Playwright TS scaffolding, POM resolver, file-snapshot discovery, HTTP worker service on `:4300`, `update-pom` AST surgery |
339
+ | v1.1 | Self-healing | `--self-healing` flag, `lib/heal.ts` template, JSONL event logging |
340
+ | v2.0 | LLM fallback | Anthropic SDK integration, governance sidecar `/sanitize`, SQLite step-binding cache, opt-in via `--llm anthropic` |
341
+ | v2.2 | Reliability | Promise.race watchdogs, ai-governance fail-closed, structured pino logging end-to-end |
342
+ | v3.0 | API testing | Native `page.request.*` patterns (~17 rules), tag-driven `@api` / `@ui` scenario state injection |
343
+ | v3.1 | TestForge P0+P1 | Visibility-prose intercept, `:root` locator rejection, `testInfo` in test signature, opt-in step hooks + boundary markers |
344
+ | v3.2 | Iteration UX | `playwright.config.ts` end-marker, exact-dep pinning, `*.spec.meta.json` sidecar, JSON scenarios input, `--merge` mode preserving `// bdd2pw:user-block` sections |
345
+ | v3.3 | Hook signature | `fixtures` arg + `status` + try/catch/finally wrapping for `beforeStep` / `afterStep` |
346
+ | v3.4 | Domain packs | Banking / healthcare / insurance opt-in rule packs (~60 new patterns) |
347
+ | v3.5 | LLM batching | One Anthropic call per scenario instead of N — ~75% cost reduction on unmatched-heavy scaffolds |
348
+ | v3.6 | Diagnostics + auto-rules | Rule-trace block in `BDD_REVIEW.md`; new `bdd2pw propose-rules` CLI clustering candidate-rules.jsonl into draft regex |
349
+ | v3.7.1 | Regression fixes | PascalCase className shadow fix; `test.step` wrapping regression-asserted |
350
+ | v3.8.1 | More domain packs | Retail / gov / education / telecom packs added — seven total (~140 industry-specific rules) |
351
+ | **v3.9.0 (current)** | Telemetry | `artefacts/llm-stats.json` sidecar — per-call latency + tokens + cache hit rate + estimated cost. Makes v3.5 batching ROI measurable per scaffold. |
352
+
353
+ ### Next 6 months (Jun 2026 → Nov 2026, ~2 releases/month)
354
+
355
+ | Target | Version | Theme | Headline |
356
+ |---|---|---|---|
357
+ | Jun 2026 | **v3.10.0** | OpenAI provider | `LLMClientOptions.provider: "openai"` lands; multi-provider client factory; same governance + cache pipeline. |
358
+ | Jul 2026 | **v3.11.0** | Gemini provider | `LLMClientOptions.provider: "gemini"` lands; full three-provider parity (Anthropic / OpenAI / Gemini). |
359
+ | Jul 2026 | **v3.12.0** | Auto-rules | `bdd2pw apply-proposals --interactive` — walks the human through each v3.6 proposal, validates the regex compiles + matches the sample texts, optionally appends to `stepMatcher.ts`. Closes the propose-rules loop. |
360
+ | Aug 2026 | **v4.0.0** | Modern defaults | Semver-major reset: bump Anthropic default model, retire v2.x deprecated APIs, switch test-signature defaults to current best practice. Migration guide ships alongside. |
361
+ | Aug 2026 | **v4.1.0** | sel2pw merge | sel2pw migrated onto `@vijaypjavvadi/pw-emit` — three packages share one emitter. All future v3.x emitter improvements automatically flow to Selenium users. |
362
+ | Sep 2026 | **v4.2.0** | Domain packs | Three more opt-in packs: **fintech** (KYC, AML, trading desks), **real-estate** (MLS, listings, escrow), **hospitality** (PMS, reservations, OTA). ~60 more rules. |
363
+ | Sep 2026 | **v4.3.0** | API v2 | WebSocket assertion rules, multipart file-upload patterns, GraphQL query / mutation / subscription rules. Extends v3.0's API testing surface. |
364
+ | Oct 2026 | **v4.4.0** | Coverage analyzer | `bdd2pw coverage` CLI: which deterministic rules fired on which features across a fleet, per-pack hit rate, dead-rule report, suggestion to retire unused rules. |
365
+ | Oct 2026 | **v4.5.0** | CI/CD templates | Optional scaffolder output: GitHub Actions / GitLab CI / Azure Pipelines / CircleCI starter workflows wired to run the generated specs. Opt-in via `--ci <provider>`. |
366
+ | Nov 2026 | **v4.6.0** | Mobile dialect | Touch / swipe / scroll-into-view rules + responsive viewport assertions. Reuses the same emitter pipeline; opt-in via `domains: ["mobile"]`. |
367
+ | Nov 2026 | **v4.7.0** | i18n Gherkin | Accept Cucumber-compatible non-English keywords (Spanish, German, French, Portuguese, Japanese, …). Lets bdd2pw scaffold from `.feature` files written in any of the ~70 languages Cucumber supports. |
368
+
369
+ Each release is shaped to be self-contained, ship in ~2 weeks, and keep `domains: []` byte-stable for existing users so the upgrade path stays painless.
370
+
371
+ ## Documents
372
+
373
+ | Doc | Purpose |
374
+ |---|---|
375
+ | [`docs/SCOPE.md`](./docs/SCOPE.md) | What's in / out of v1.0, FR/NFR, CLI + HTTP surface, decisions log |
376
+ | [`docs/ARCHITECTURE.md`](./docs/ARCHITECTURE.md) | Component diagram, sequence diagrams, `pw-emit` contract, HTTP API contract, error model |
377
+ | [`docs/PHASE_0_PLAN.md`](./docs/PHASE_0_PLAN.md) | The pw-emit extraction plan (mostly historical now — it shipped) |
378
+ | [`docs/STATUS.md`](./docs/STATUS.md) | Verified state right now — what works, what's pending, what's deferred |
379
+ | [`CHANGELOG.md`](./CHANGELOG.md) | Full version history |
380
+ | [`CONTRIBUTING.md`](./CONTRIBUTING.md) | Local dev, branching, PR conventions |
381
+
382
+ ## Examples
383
+
384
+ - [`examples/practice-test-login/`](./examples/practice-test-login/) — first-person dialect, `Scenario Outline + Examples`, security scenarios. Validated 7/7 green against a live public site.
385
+ - [`examples/cms-login/`](./examples/cms-login/) — third-person dialect, compound input, `is redirected`. From a real OSS Selenium codebase.
386
+ ## Citation
387
+
388
+ If you use `@vijaypjavvadi/bdd2pw` in academic work, please cite:
389
+
390
+ > Javvadi, V. P. (2026). *@vijaypjavvadi/bdd2pw: Live-DOM Page Object Scaffolding from Gherkin Specifications via the Microsoft Playwright MCP* (Version 3.8.1) [Computer software]. Zenodo. https://doi.org/10.5281/zenodo.20450278
391
+
392
+ A machine-readable [`CITATION.cff`](CITATION.cff) file is included in the repository root.
393
+
394
+ ## License
395
+
396
+ MIT