@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/CHANGELOG.md ADDED
@@ -0,0 +1,308 @@
1
+ # bdd2pw — CHANGELOG
2
+
3
+ All notable changes to this project will be documented in this file.
4
+
5
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
+ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
+
8
+ ## [Unreleased]
9
+
10
+ _Nothing yet._
11
+
12
+ ## [1.0.0] — 2026-05-03
13
+
14
+ First public npm release. Six phases shipped, two real-world fixtures locked,
15
+ 101 / 101 tests green. See [`docs/STATUS.md`](./docs/STATUS.md) for the runtime
16
+ snapshot and [`docs/SCOPE.md`](./docs/SCOPE.md) §17 for the full
17
+ implementation-time decisions log.
18
+
19
+ ### Headline
20
+
21
+ `feature in → ready-to-run Playwright tests out`. Rule-based
22
+ deterministic generation (no LLM in v1.0). CLI + HTTP service. Same code path
23
+ through both surfaces.
24
+
25
+ ### Shipped in v1.0
26
+
27
+ - **Phase 0** — `@vijaypjavvadi/pw-emit@1.0.0` extracted; bdd2pw consumes it as
28
+ a regular dep (`^1.0.0`).
29
+ - **Phase 1a** — Gherkin parser (`@cucumber/gherkin`), repo scanner (ts-morph),
30
+ POM resolver (reuse / augment / create), file-snapshot discovery, locator
31
+ picker (priority chain + status-region special case), 14-rule step matcher,
32
+ emitter facade, end-to-end `scaffold()`. Locked against `practice-test-login`
33
+ (7/7 mapped, 0 warnings).
34
+ - **Phase 1b** — Live browser discovery via direct `playwright.chromium.launch()`
35
+ (chose direct Playwright over `@playwright/mcp` for simpler in-process control).
36
+ Status-region visibility-filter bypass.
37
+ - **Phase 1c** — Second real fixture `cms-login` (third-person dialect),
38
+ 0 warnings first try. Subject-agnostic prefix locked.
39
+ - **Phase 2** — `analyze` (dry-run) + `update-pom` (append-only AST surgery via
40
+ ts-morph; hard-fails on missing POM). `BDD_REVIEW.md` writer. `tsc --noEmit`
41
+ validator (soft-fail).
42
+ - **Phase 3** — Express HTTP service `:4300`. Three async workers, ULID job
43
+ IDs, Zod request validation, Pino structured logs, archiver-streamed zip
44
+ artefacts. Per-job tmpdir contains blast radius for HTTP scaffold runs.
45
+
46
+ ### CLI
47
+
48
+ ```
49
+ bdd2pw scaffold <feature> --url <url> --page <Name> --repo <dir> [opts]
50
+ bdd2pw analyze <feature> --url <url> [opts]
51
+ bdd2pw update-pom --page <Name> --url <url> --repo <dir>
52
+ bdd2pw serve [--port 4300]
53
+ ```
54
+
55
+ ### HTTP
56
+
57
+ `POST /scaffold | /analyze | /update-pom` → `202 + { jobId, links }`.
58
+ `GET /jobs/:id`, `GET /jobs/:id/artifact` (zip), `GET /jobs/:id/log`,
59
+ `GET /healthz`, `GET /readyz`, `GET /version`.
60
+
61
+ ### Tests
62
+
63
+ 101 / 101 across 10 Vitest files. Includes a full HTTP regression that asserts
64
+ the artefact response body starts with the ZIP magic bytes `PK\x03\x04`.
65
+
66
+ ### Distribution
67
+
68
+ - npm: `@vijaypjavvadi/bdd2pw@1.0.0` (public, scoped, with provenance).
69
+ - GitHub: tag-triggered `release.yml` workflow auto-publishes on `vX.Y.Z` push.
70
+ - CI matrix: Ubuntu / macOS / Windows × Node 18 / 20 / 22.
71
+
72
+ ### Deferred to v1.1+
73
+
74
+ - LLM fallback for vague steps (governance-routed). Real fixtures hit 100%
75
+ rule coverage so this was off the critical path.
76
+ - SQLite-backed job persistence (currently in-memory `Map`).
77
+ - Built-in `auth.setup.ts` generator. Today: bring your own `--storage-state`.
78
+ - Multi-page navigation discovery, `playwright-bdd` runtime mode, API mocking
79
+ scaffolds, visual regression — all post-v1.1.
80
+
81
+ ### Earlier-phase notes (kept for history)
82
+
83
+ #### Phase 3 — HTTP service is real
84
+
85
+ The `POST /scaffold`, `POST /analyze`, `POST /update-pom` routes now actually do work. Same code path as the CLI; HTTP is a thin shell.
86
+
87
+ #### What landed
88
+
89
+ - **Workers in `src/http/routes.ts`** — three async `Promise` workers, one per `POST` endpoint. Each accepts the parsed Zod-validated body, calls the matching `scaffold()` / `analyze()` / `updatePom()` function from `src/index.ts`, and updates the in-memory job state through `running` → `completed` / `failed`.
90
+ - **Per-job artifact dir.** `POST /scaffold` overrides the caller-provided `repo` with `<os.tmpdir()>/bdd2pw-jobs/<jobId>/`. This contains the blast radius — the HTTP user can't write outside that sandbox even if they pass an unsafe path.
91
+ - **`streamArtifactZip()` in `src/http/artifacts.ts`** — uses `archiver` to zip the artifact dir on the fly, streaming directly to the response. Excludes `node_modules`, `.git`, `test-results`, `playwright-report`. Headers: `application/zip` + `Content-Disposition: attachment; filename="bdd2pw-<jobId>.zip"`.
92
+ - **`GET /jobs/:id/log`** — returns a plain-text dump of the job's warnings + errors. Real per-job pino log file tailing deferred to v1.1.
93
+ - **`GET /jobs/:id/artifact`** failure modes covered: `404 JobNotFound`, `409 ArtifactNotReady` (job still running or failed), `410 ArtifactExpired` (artifact dir already swept).
94
+
95
+ #### Tests
96
+
97
+ - `tests/e2e/httpWorker.test.ts` — spins up the real Express app on a random port, drives it via `fetch()`, polls for completion. 11 assertions across 4 describe blocks:
98
+ - `/healthz`, `/version`, `/readyz` respond correctly.
99
+ - `POST /scaffold` validates the body and returns 400 for malformed input (missing fields, bad URL).
100
+ - End-to-end: POST `/scaffold` with the practice-test-login fixture → poll `/jobs/:id` → reaches `completed` with no warnings → `GET /jobs/:id/artifact` returns a real zip (verified by the ZIP magic bytes `PK\x03\x04` at the start of the response body).
101
+ - `GET /jobs/:id/log` returns a text/plain dump containing the job ID and status.
102
+
103
+ #### Out of scope for v1.0 (deferred to v1.1+)
104
+
105
+ - **Multi-tenancy / auth** — handled at the gateway (`modern-automation-platform:3000`), bdd2pw HTTP is single-tenant per process. Same pattern as `sel2pw`.
106
+ - **Job queue + worker pool** — current model is one Promise per job, bounded by Node's event loop. SQLite-backed queue with `p-limit` on MCP browsers lands in v1.1.
107
+ - **Granular per-stage progress events** — workers emit `parsing` / `emitting` / `completed`, but not the full 9-stage progress chain from the architecture doc. Sufficient for the UI to show "running" → "done"; finer-grained progress is a polish task.
108
+ - **Real per-job log file** — `GET /jobs/:id/log` currently returns warnings/errors from the in-memory record. Pino multi-stream routing per job is v1.1.
109
+
110
+ ### Phase 2 — `updatePom` ships, append-only by construction
111
+
112
+ Replaces the `NotImplementedError` stub. **The third CLI command is now real.** Re-scanning a page that already has a Page Object merges newly-discovered locators in — without ever touching what's already there.
113
+
114
+ #### Design choice: append-only over lock-blocks
115
+
116
+ The original SCOPE proposed `// __BDD2PW_LOCK__` markers around generated content. Switched to **append-only via AST surgery** because:
117
+
118
+ - No visible markers in user code (lock comments are noise in otherwise hand-readable files).
119
+ - Stronger guarantee: enforced by construction, not by comments the user could strip.
120
+ - No special-casing for hand-edits — if it's already there, it's preserved.
121
+ - Smaller blast radius: bugs in updatePom can only ADD wrong things, never destroy existing code.
122
+
123
+ Trade-off: stale fields (when the live page removes an element) are not auto-deleted. Intentional — staleness is recoverable, deletion is not.
124
+
125
+ #### Hard rules `updatePom` enforces
126
+
127
+ 1. Never deletes any property, method, or import.
128
+ 2. Never renames anything.
129
+ 3. Never modifies any existing method body — hand-edits are preserved byte-identical.
130
+ 4. Skips field-name collisions (existing field wins).
131
+ 5. Method synthesis is opt-in and deferred to v1.1.
132
+ 6. Hard-fails if the POM file doesn't exist (suggests `scaffold` first).
133
+ 7. Hard-fails if the class name doesn't match `--page`.
134
+
135
+ #### Implementation
136
+
137
+ - **`updatePom()` in `src/index.ts`** — replaces the stub. Uses `ts-morph` to read the existing POM AST, computes the new-fields diff against the live scan, and uses `addProperty` + `addStatements` to splice new content into the existing class. Then `save()` writes back. No re-emit, no overwrite.
138
+ - **`renderLocatorExpr` imported from `@vijaypjavvadi/pw-emit`** so the appended constructor lines use the same locator format as `scaffold` would.
139
+ - Honours `--no-discovery` and `--snapshot-file` flags for offline/CI use, mirroring `scaffold`.
140
+ - Surfaces a `BDD_REVIEW.md` with diff stats: existing/discovered/new/skipped-collisions counts.
141
+
142
+ #### Tests
143
+
144
+ - `tests/e2e/updatePom.test.ts` — 11 assertions across 3 describe blocks. The critical cases:
145
+ - **Hand-edits survive byte-identical** (the must-not-regress invariant): hand-written comments, `console.log` lines, and entire method bodies stay untouched.
146
+ - **Custom helper methods survive** — anything outside the discovered IR is preserved.
147
+ - **Existing field collisions are skipped** — if the user already declared `usernameInput` with a `#username` selector, updatePom does NOT overwrite with whatever the live snapshot would pick.
148
+ - **Idempotent** — second invocation in a row adds zero fields.
149
+ - **Hard-fail on missing class** — points at `scaffold`.
150
+
151
+ ### Phase 1c — second real-world fixture clean (selenium14/LoginCMS)
152
+
153
+ **Validated against `selenium14/AutomationFrameworkCucumberTestNG/src/test/resources/features/LoginCMS.feature` — third-party Cucumber dialect from a real OSS Selenium codebase, against `https://cms.anhtester.com/login`.** 0 warnings, 0 errors, every step mapped.
154
+
155
+ This fixture exercises:
156
+ - `User navigate to Login Page for Admin "URL"` — third-person subject + bare-text + quoted URL → `goto()`
157
+ - `user enter email "admin@example.com" password "123456"` — **compound input step** (one Gherkin step, two field/value pairs) → two `.fill()` lines emitted
158
+ - `click Login button` — no subject prefix → `loginButton.click()`
159
+ - `user is redirected to the Dashboard page` — third-person `is redirected to` → `toHaveURL(/Dashboard/)`
160
+
161
+ #### Bugs fixed this round
162
+
163
+ - **Step matcher: subject-agnostic rules.** All 13 rules now accept `I | user | User | the user | the User` as the subject prefix (was hard-coded `I`). Verb forms also accept singular AND plural conjugation (`click | clicks`, `enter | enters`, etc.). For rule 3 (click) the subject is now optional entirely (`click Login button` works without any subject).
164
+ - **Rule 1 (navigate): handles `navigate to <text> "<URL>"`.** Previous pattern only accepted `navigate to <target>`. New pattern: `(?:.+?\s+)?["']?<target>["']?` — the optional `<text> ` prefix is consumed, the quoted URL becomes the target.
165
+ - **Rule 11 (redirect): accepts `is redirected`.** Was `(should be |am )?redirected`; now `(should be |am |is )?redirected`.
166
+ - **Compound input rule (2c).** New rule for `SUBJ verb FIELD1 "V1" FIELD2 "V2" [...]` — extracts every (field, value) pair via a global regex pass and emits one `.fill()` per pair. Falls back to `null` (TODO) if any field doesn't resolve, so partial matches don't produce wrong code.
167
+
168
+ #### Type addition (small)
169
+
170
+ - **`StepBinding.customBody?: string`** — for compound steps that produce multiple TS statements per Gherkin step. The emitter writes `customBody` verbatim instead of synthesising a single line from `pomCall`/`assertion`. Existing rules unchanged; only the new compound rule uses this path.
171
+
172
+ #### Orchestrator bug fix
173
+
174
+ - **Background warnings now flow into `BDD_REVIEW.md`.** Previously only scenario-step warnings were collected; Background-step warnings were silently dropped. Now surfaced under `[Background]`.
175
+
176
+ ### Phase 1b milestone — live discovery 7/7 against the real site
177
+
178
+ **Same `practice-test-login` feature that needed a hand-authored snapshot in Phase 1a now passes 7/7 with `--url` only — bdd2pw scans the live page itself.** No snapshot file, no hand-tuning, real Chromium against `https://practicetestautomation.com/practice-test-login/`.
179
+
180
+ #### Iteration arc (live-scan rounds)
181
+
182
+ | Round | Pass / Fail | Root cause + fix |
183
+ |---|---|---|
184
+ | 1 | 4 / 3 | Scanner missed `<div id="error">` (no role/testid/aria-label) → broaden selector list to include `[id*=error i]`, `[class*=alert i]`, etc. |
185
+ | 2 | 3 / 4 | Scanner picked up error div but field name was text-derived (`yourUsernameIsInvalid`) → step matcher's `findField("error", [...])` couldn't match it. Fix: in `synthFieldName`, when CSS id matches `STATUS_REGION_RE`, prefer id-derived name → field becomes `error`. Plus `isLikelyStatusRegion` bypasses the visibility filter (status banners are intentionally hidden until triggered). Plus implicit roles for `<button>`/`<a>`/`<input type=submit>` so picker emits `getByRole`. Plus dedup-by-locator-value (collapse `username` + `usernameInput` into one). |
186
+ | 3 | 3 / 4 | Field name was right (`error`) but locator was wrong: picker emitted `getByText("Your username is invalid!")` not `locator("#error")`. Volatile text content was winning over the stable id. Fix: locator picker skips the `getByText` branch when element matches `STATUS_REGION_RE` — text changes per scenario, id is the stable handle. |
187
+ | 4 | **7 / 0** | — |
188
+
189
+ #### Bugs fixed this round
190
+
191
+ - **`src/discovery/mcpClient.ts`:** broader selector list (`[id*=error]`, `[class*=alert]`, …); `implicitRole()` for native HTML form controls; `isLikelyStatusRegion()` visibility-filter bypass; `isVisible()` zero-size + display:none filter; tsconfig `/// <reference lib="dom" />` so the `page.evaluate()` callback type-checks.
192
+ - **`src/transformers/locatorPicker.ts`:** new `STATUS_REGION_RE`; `synthFieldName` priority 0 = id-derived for status regions (so `<div id="error">` → `error`, not `yourUsernameIsInvalid`); `getByText` branch skipped for status regions; `dedupeLocators` now collapses by `(api, args)` identity, keeping the longer field name.
193
+
194
+ #### What this proves
195
+
196
+ Three of the original Phase 1a iteration rounds were **snapshot accuracy** bugs (fictional ARIA roles, fictional placeholders). With Phase 1b they're gone — the scanner sees what's actually there. The four Phase 1b rounds were rule/picker bugs surfaced by real DOM data, all fixed. Net result: a **single CLI invocation** with `--url` produces a runnable spec from a Cucumber `.feature` file with zero hand-edits, zero snapshot authoring.
197
+
198
+ ### Phase 1b — real browser-based page discovery (initial implementation, earlier this round)
199
+
200
+ Replaces the file-snapshot-only discovery with a real Chromium-launching scanner. Hand-authoring `snapshot.json` is no longer required for the common case; pass `--url` and bdd2pw scans the live page itself.
201
+
202
+ #### What landed
203
+
204
+ - **`scanPageWithBrowser()`** in `src/discovery/mcpClient.ts` — uses `playwright.chromium.launch()` directly. Walks the DOM with a curated selector list (inputs, buttons, links, selects, headings, anything with `[role]` / `[data-testid]` / `[aria-label]`), extracts per-element role / accessible name / label / placeholder / testId / text / best CSS selector, and returns a flat `ElementIR[]`-shaped list. Snapshot parser handles it without changes.
205
+ - **`playwright`** added as an `optionalDependencies` entry. Dynamic-imported in the scanner with a clear install-hint error if missing (`"npm install -D playwright && npx playwright install chromium"`).
206
+ - **Storage state support** — pass `--storage-state <path.json>` to scan an authenticated page; passed through to `browser.newContext({ storageState })`.
207
+ - **Headed mode** — `--headed` shows the browser window, useful for debugging snapshot accuracy.
208
+ - **Network-idle wait with fallback** — first attempt waits 5s for network idle (catches SPAs that finish loading lazily); falls back to `domcontentloaded` if idle never settles.
209
+
210
+ #### Removed framing
211
+
212
+ - Dropped `@playwright/mcp` from optional dependencies. **AQ-2 resolved:** MCP is an LLM-control protocol, not a programmatic browser API. We just need one URL → one snapshot, so direct `playwright` is one fewer process and one fewer protocol layer. The file `src/discovery/mcpClient.ts` keeps its name for now (rename is cosmetic, deferred).
213
+
214
+ #### Backward compatibility
215
+
216
+ - `--snapshot-file <path>` still works exactly as before. The orchestrator dispatches: snapshot file present → file path; otherwise → real browser. CI runs that don't want network access keep using pinned snapshot files (e.g. `examples/practice-test-login/snapshot.json`).
217
+ - `practiceTestLogin.test.ts` still uses the file-snapshot path so the regression test runs without network.
218
+
219
+ #### What this fixes
220
+
221
+ The 3-of-6 iteration rounds in Phase 1a milestone that were "snapshot accuracy" bugs (fictional ARIA roles, fictional placeholders, etc.) — those scenarios should now Just Work because the scanner sees the actual DOM.
222
+
223
+ ### Phase 1a milestone — first end-to-end green run (earlier this round)
224
+
225
+ **The full rule-based pipeline produced a runnable Playwright spec from a real-world Cucumber `.feature` file, with zero hand-edits.** Validated against `https://practicetestautomation.com/practice-test-login/` — `npx playwright test --project=chromium` reports **7 passed (4.2s)**.
226
+
227
+ #### Scope
228
+
229
+ - Source feature: `examples/practice-test-login/login.feature` — 5 scenarios + 1 Scenario Outline (2 example rows) + Background = 7 generated `test()` blocks
230
+ - Discovery source: hand-authored `examples/practice-test-login/snapshot.json` (Phase 1a fallback; real `@playwright/mcp` lands in Phase 1b)
231
+ - Output target: `pages/login.page.ts`, `tests/login.spec.ts`, `playwright.config.ts`, `package.json`, `tsconfig.json`, `.gitignore`, `BDD_REVIEW.md`
232
+
233
+ #### Iteration arc that got us there
234
+
235
+ | Round | Pass / Fail | Root cause |
236
+ |---|---|---|
237
+ | 1 | 0 / 7 | Undeclared `loginPage` (facade emitted POM calls without instantiation) |
238
+ | 2 | 4 / 3 | Rule 3 lost role hint; rule 8 required "the" not "an"; word-order mismatch in rule 2 |
239
+ | 3 | 2 / 5 | Snapshot fictional ARIA roles (`role: "alert"` not on page) |
240
+ | 4 | 1 / 6 | Snapshot fictional placeholders (`placeholder: "Username"` not on page) |
241
+ | 5 | 6 / 1 | Rule 9 emitted `toHaveText` (exact); step said "containing" |
242
+ | 6 | **7 / 0** | (none) |
243
+
244
+ 3 of 6 rounds were rule/code bugs. 3 were snapshot accuracy — exactly what real `@playwright/mcp` integration will eliminate (Phase 1b).
245
+
246
+ #### Bugs fixed this round (rule + code)
247
+
248
+ - **`emitTestFile` facade (`src/emitters/facade.ts`):** prepend `const <pageVar> = new <Class>(page);` to every test body and every hook body. Previously the spec emitted `await loginPage.goto()` without ever declaring `loginPage`.
249
+ - **`scaffold()` orchestrator (`src/index.ts`):** synthesise a `goto(): Promise<void>` POM method that calls `await this.page.goto(<url>)` when no goto exists. Without it, rule 1 emitted `await loginPage.goto()` against a method with no body.
250
+ - **POM file naming (`src/index.ts`):** use `pageObjectFileName` from `@vijaypjavvadi/pw-emit` instead of `${camelCase(opts.page)}.page.ts`. Was emitting `loginPage.page.ts`; now emits `login.page.ts`.
251
+ - **Reserved field-name filter (`src/index.ts`):** `page`, `context`, `browser` are now excluded from the "missing fields" set. Previously `loginPage.page.toHaveURL(...)` made `page` look like a missing field.
252
+ - **Step matcher rule 2b (`src/transformers/stepMatcher.ts`):** new — handles reversed word order `I enter <field> "<value>"`. Original rule expected `I enter "<value>" into <field>`.
253
+ - **Step matcher rule 3:** captures the explicit role suffix (`button` / `link` / `icon` / `tab`) and passes only that to `findField` so "click the login button" doesn't match `testLoginHeading`.
254
+ - **Step matcher rule 8:** accepts `the | an | a error message`, prefers `Alert > Error > Message`-suffixed fields.
255
+ - **Step matcher rule 9a:** new — `toContainText` semantics for `containing | that contains | with text`. Must run before rule 9b (exact text equality) which now also accepts `the | an | a`.
256
+ - **Step matcher rules 11–13:** new — redirect (`toHaveURL` regex), attribute check (`toHaveAttribute`), URL prefix/contains (`toHaveURL` anchored regex).
257
+ - **`findField` resolution order (`src/transformers/stepMatcher.ts`):** added a suffix-constrained pass — when preferredSuffixes are provided, only consider fields with those suffixes; if exactly one matches, use it. Stops "login" from matching `testLoginHeading` when there's a `submitButton` on the same page.
258
+ - **`synthFieldName` (`src/transformers/locatorPicker.ts`):** added `cssSelectorToName()` fallback. When no name/label/placeholder/text is available, derive the field name from the CSS id (`#username` → `usernameInput`) instead of the ugly `inputElement` placeholder.
259
+
260
+ #### Added
261
+
262
+ - `examples/practice-test-login/` — pinned regression fixture (login.feature + snapshot.json + README)
263
+ - `tests/e2e/practiceTestLogin.test.ts` — 22-assertion regression test for the full pipeline. Calls `scaffold()`, reads back POM + spec, asserts every binding pattern that should have landed.
264
+ - `vitest.config.ts` — `hookTimeout: 30_000` for E2E test cold-start reads.
265
+
266
+ ### Added — Phase 1a (initial implementation, earlier this round)
267
+ - **Real Gherkin parser** — `src/parser/gherkinParser.ts`. Uses `@cucumber/gherkin`. Handles Background, Scenario, Scenario Outline + Examples, doc strings, data tables.
268
+ - **Step matcher rules** — `src/transformers/stepMatcher.ts`. 10 rules covering navigation, input, click, select, check/uncheck, visibility (positive + negative), text equality, URL, error message. Falls through to a warning when no rule matches.
269
+ - **Locator picker** — `src/transformers/locatorPicker.ts`. Priority chain `getByRole > getByLabel > getByPlaceholder > getByTestId > getByText > css > xpath`. Ambiguity flagged when rivals exist. Field-name synthesis with role-based suffixes (`usernameInput`, `signInButton`).
270
+ - **POM resolver** — `src/transformers/pomResolver.ts`. Pure decision: REUSE / AUGMENT / CREATE based on existing fields vs referenced fields.
271
+ - **Repo scanner** — `src/repo/repoScanner.ts`. `ts-morph`-based AST scan of `pages/*.page.ts`. Extracts class name, locator fields, methods (preserves bodies for AUGMENT mode).
272
+ - **Project scaffolder** — `src/repo/projectScaffolder.ts`. Delegates to `@vijaypjavvadi/pw-emit`'s `emitProject()`. Idempotent.
273
+ - **Page discovery (file-snapshot fallback)** — `src/discovery/mcpClient.ts`. Phase 1a reads a pre-captured JSON snapshot via `--snapshot-file`. Real `@playwright/mcp` integration scheduled for Phase 1b.
274
+ - **Snapshot parser** — `src/discovery/snapshotParser.ts`. Accepts both flat-array and tree-shaped snapshots; flattens to `ElementIR[]`.
275
+ - **Review report** — `src/reports/reviewReport.ts`. Writes `BDD_REVIEW.md` with severity sections (Errors / Warnings / Info).
276
+ - **TSC validator** — `src/validate/tscRunner.ts`. Runs `tsc --noEmit` in the target repo, parses diagnostics into `ReviewItem`s. Soft-fails with a warning if `tsc` isn't installed.
277
+ - **`scaffold()` end-to-end wired** — `src/index.ts`. Orchestrates: parse → scan repo → scaffold → discover → pick locators → match steps (provisional) → resolve POM → re-match against final POM → expand Scenario Outlines → emit POM + spec → tsc validate → review report.
278
+ - **`analyze()` working** — same pipeline minus emit/validate. Returns the plan.
279
+ - **CLI `--snapshot-file` flag** for the file-based discovery path.
280
+ - **Example fixture** `examples/login-feature/snapshot.json` — hand-authored a11y tree for end-to-end testing.
281
+ - **3 new unit test files**: `stepMatcher.test.ts`, `locatorPicker.test.ts`, `pomResolver.test.ts`.
282
+
283
+ ### Still stubbed (next phases)
284
+ - `updatePom()` — needs lock-block enforcement (Phase 2)
285
+ - Real Playwright MCP integration (Phase 1b)
286
+ - HTTP worker that calls `scaffold()` (Phase 3)
287
+ - LLM fallback in stepMatcher (Phase 4)
288
+
289
+ ### Added — Phase 0 (previous round)
290
+ - Repository scaffold: package.json, tsconfig, ESLint, Prettier, Vitest, TypeDoc, MIT LICENSE
291
+ - CI workflow (Node 18/20/22 × Ubuntu/macOS/Windows), Release workflow (tag-triggered npm publish with provenance)
292
+ - `.changeset` config for versioning
293
+ - `src/` skeleton: `cli.ts`, `server.ts`, `index.ts`, `types.ts`, `utils/{naming,logger}.ts`
294
+ - Module stubs (now most replaced by Phase 1a implementations)
295
+ - Project templates: `package.json.tmpl`, `playwright.config.ts.tmpl`, `tsconfig.json.tmpl`, `gitignore.tmpl`
296
+ - Example fixture: `examples/login-feature/login.feature`
297
+ - First passing unit test: `tests/unit/naming.test.ts`
298
+ - `@vijaypjavvadi/pw-emit` consumed via `file:../pw-emit`
299
+ - `src/emitters/facade.ts` rewritten to delegate to `pw-emit`
300
+
301
+ ### Documents
302
+ - `README.md` — project orientation, decisions log, phasing
303
+ - `docs/SCOPE.md` — v1.0 scope, FR/NFR, CLI + HTTP surface
304
+ - `docs/ARCHITECTURE.md` — component diagram, sequence flows, contracts, error model
305
+
306
+ ## [0.1.0] — TBD
307
+
308
+ Initial published placeholder. **No runtime functionality yet.** Phase 0 (extracting `@vijaypjavvadi/pw-emit` from `sel2pw`) blocks Phase 1 of this package.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Vijay P. Javvadi
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,254 @@
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 14-rule matcher 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
+ ## CLI surface — three commands
62
+
63
+ ### `scaffold` — generate a fresh repo
64
+
65
+ ```bash
66
+ bdd2pw scaffold <feature> --url <url> --page <Name> --repo <dir> [options]
67
+ ```
68
+
69
+ Reads the `.feature`, scans the URL with Chromium, picks locators, matches steps, emits the project. Flags:
70
+
71
+ ```
72
+ --snapshot-file <path> Use a captured JSON snapshot instead of launching a browser
73
+ --no-discovery Skip page discovery entirely (rule-only probing)
74
+ --storage-state <path> Pre-authenticated storage state JSON
75
+ --headed Show browser during scan
76
+ --templates <dir> Override default project templates
77
+ --dry-run Print plan, write nothing
78
+ --no-validate Skip tsc --noEmit step
79
+ --force Overwrite existing spec files
80
+ ```
81
+
82
+ ### `analyze` — dry-run the matcher
83
+
84
+ ```bash
85
+ bdd2pw analyze <feature> --url <url> [options]
86
+ ```
87
+
88
+ 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.
89
+
90
+ ### `update-pom` — merge new locators into an existing POM
91
+
92
+ ```bash
93
+ bdd2pw update-pom --page <Name> --url <url> --repo <dir> [options]
94
+ ```
95
+
96
+ 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.
97
+
98
+ ## HTTP service
99
+
100
+ `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.
101
+
102
+ ```bash
103
+ bdd2pw serve --port 4300
104
+
105
+ # In another terminal:
106
+ curl -X POST http://localhost:4300/scaffold \
107
+ -H 'content-type: application/json' \
108
+ -d '{"feature":"/abs/path/to/login.feature","url":"https://...","page":"LoginPage","repo":"unused","options":{"snapshotFile":"/abs/snapshot.json","noValidate":true}}'
109
+ # → 202 Accepted: {"jobId":"01HX...","links":{"self":"/jobs/01HX...","artifact":"/jobs/01HX.../artifact"}}
110
+
111
+ curl http://localhost:4300/jobs/01HX... # poll for completion
112
+ curl -O http://localhost:4300/jobs/01HX.../artifact # download zip
113
+ ```
114
+
115
+ | Endpoint | Purpose |
116
+ |---|---|
117
+ | `GET /healthz`, `/version`, `/readyz` | Liveness probes |
118
+ | `POST /scaffold`, `/analyze`, `/update-pom` | Async job — returns `202` + `jobId` |
119
+ | `GET /jobs/:id` | Job status / progress / result |
120
+ | `GET /jobs/:id/artifact` | Stream zip of the scaffolded repo |
121
+ | `GET /jobs/:id/log` | Plain-text dump of warnings + errors |
122
+
123
+ In production, the platform gateway proxies `/api/v1/scaffold/*` to `:4300`. Direct port exposure is local-dev only.
124
+
125
+ ## Pipeline
126
+
127
+ ```
128
+ .feature file
129
+ │
130
+ ▼
131
+ ┌──────────────┐
132
+ │ gherkinParser│ @cucumber/gherkin → FeatureIR (scenarios, background, examples)
133
+ └──────┬───────┘
134
+ ▼
135
+ ┌──────────────┐
136
+ │ repoScanner │ ts-morph → existing pages/*.page.ts → Map<class, IR>
137
+ └──────┬───────┘
138
+ ▼
139
+ ┌──────────────┐
140
+ │ pomResolver │ CREATE / AUGMENT / REUSE decision
141
+ └──────┬───────┘
142
+ ▼
143
+ ┌──────────────┐
144
+ │ scanPage │ playwright chromium → DOM walk → ElementIR[]
145
+ │ │ (or --snapshot-file / --no-discovery fallbacks)
146
+ └──────┬───────┘
147
+ ▼
148
+ ┌──────────────┐
149
+ │ locatorPicker│ getByRole > getByLabel > getByPlaceholder > getByTestId
150
+ │ │ > getByText > css > xpath
151
+ │ │ Status-region special case + dedup by (api, args)
152
+ └──────┬───────┘
153
+ ▼
154
+ ┌──────────────┐
155
+ │ stepMatcher │ 14 rules — subject-agnostic, dialect-tolerant,
156
+ │ │ compound-input via customBody
157
+ └──────┬───────┘
158
+ ▼
159
+ ┌──────────────┐
160
+ │ pw-emit │ @vijaypjavvadi/pw-emit → POM TS + spec TS + project scaffold
161
+ └──────┬───────┘
162
+ ▼
163
+ ┌──────────────┐
164
+ │ tscRunner │ Optional tsc --noEmit → diagnostics into BDD_REVIEW.md
165
+ └──────┬───────┘
166
+ ▼
167
+ output/
168
+ pages/<name>.page.ts
169
+ tests/<name>.spec.ts
170
+ playwright.config.ts
171
+ package.json, tsconfig.json, .gitignore
172
+ BDD_REVIEW.md ← warnings + manual TODOs
173
+ ```
174
+
175
+ ## What `bdd2pw` covers
176
+
177
+ ### Step matcher rules (14)
178
+
179
+ | # | Pattern | Emits |
180
+ |---|---|---|
181
+ | 1 | `(I/user) am on/navigate to/visit/open <X>` (with or without quoted URL) | `goto()` |
182
+ | 2a | `(I/user) enter "<value>" into <field>` | `<field>.fill("<value>")` |
183
+ | 2b | `(I/user) enter <field> "<value>"` | `<field>.fill("<value>")` |
184
+ | 2c | **Compound:** `(I/user) enter <f1> "<v1>" <f2> "<v2>" ...` | N `.fill()` lines |
185
+ | 3 | `(I/user/-) click/press/tap <X> button/link/icon/tab` | `<field>.click()` (or POM method if defined) |
186
+ | 4 | `(I/user) select "<opt>" from <dropdown>` | `<field>.selectOption(...)` |
187
+ | 5 | `(I/user) check/uncheck <checkbox>` | `<field>.check()` / `.uncheck()` |
188
+ | 6 | `(I/user) (should) see <X>` | `expect(<X>).toBeVisible()` |
189
+ | 7 | `(I/user) should not see <X>` | `expect(<X>).toBeHidden()` |
190
+ | 8 | `(I/user) should see (the/an/a) error message "<X>"` | `expect(<errorField>).toContainText("<X>")` |
191
+ | 9a | `(I/user) should see <X> containing/with text "<Y>"` | `expect(<X>).toContainText("<Y>")` |
192
+ | 9b | `(I/user) should see <X> "<Y>"` (exact equality) | `expect(<X>).toHaveText("<Y>")` |
193
+ | 10 | `(I/user) should remain on <page>` | `expect(page).toHaveURL(/<page>/)` |
194
+ | 11 | `(I/user) (should be/is) redirected to <page>` | `expect(page).toHaveURL(/<page>/)` |
195
+ | 12 | `the <field> field should be of type "<type>"` | `expect(<field>).toHaveAttribute("type", "<type>")` |
196
+ | 13 | `the (current) URL should start with/contain "<X>"` | `expect(page).toHaveURL(/^<X>/)` |
197
+
198
+ ### Cucumber features
199
+
200
+ - `Background:` → `test.beforeEach`
201
+ - `Scenario Outline:` + `Examples:` → one `test()` per row, with placeholder substitution
202
+ - `Tags` → emitted as `// @tag` comments above each test
203
+ - Doc strings + data tables — preserved on `StepIR.argument`
204
+
205
+ ### Locator priority
206
+
207
+ `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).
208
+
209
+ ## Verification
210
+
211
+ | Check | Status |
212
+ |---|---|
213
+ | `npm run lint` | ✅ clean |
214
+ | `tsc --noEmit` | ✅ clean |
215
+ | `npm test` (vitest) | ✅ **101/101 green** across 10 test files |
216
+ | `practice-test-login` end-to-end vs the live site | ✅ **7/7** scenarios green via `npx playwright test` |
217
+ | `cms-login` (selenium14 dialect) regression | ✅ 12 assertions green, third-person + compound input + `is redirected` |
218
+ | HTTP worker zip download | ✅ end-to-end test verifies real ZIP magic bytes |
219
+ | `update-pom` preserves hand-edited method bodies | ✅ 5 byte-identical-survival assertions |
220
+
221
+ ## Roadmap
222
+
223
+ | Phase | Status | Headline |
224
+ |---|---|---|
225
+ | Phase 0 | ✅ Shipped | `@vijaypjavvadi/pw-emit` extracted; sibling package consumed by both `bdd2pw` and (planned) `sel2pw v1.2` |
226
+ | Phase 1a | ✅ Shipped | Gherkin parser, step matcher rules, locator picker, POM resolver, repo scanner, file-snapshot discovery, scaffold orchestration |
227
+ | Phase 1b | ✅ Shipped | Real Chromium-based page discovery via `playwright`, dialect-agnostic rules |
228
+ | Phase 1c | ✅ Shipped | Third-person + compound-input dialect coverage; second real-world fixture pinned |
229
+ | Phase 2 | ✅ Shipped | `update-pom` with append-only AST surgery — preserves hand-edits byte-identical |
230
+ | Phase 3 | ✅ Shipped | HTTP service `:4300` with three real workers + zip artifact download |
231
+ | **v1.0 (this release)** | **Ready to tag** | All of the above |
232
+ | v1.1 | Roadmap | LLM fallback for genuinely-vague steps, governance-routed |
233
+ | v1.2 | Roadmap | sel2pw migration onto `pw-emit`; multi-page scenario discovery |
234
+ | v1.3 | Roadmap | Optional `playwright-bdd` runtime mode |
235
+
236
+ ## Documents
237
+
238
+ | Doc | Purpose |
239
+ |---|---|
240
+ | [`docs/SCOPE.md`](./docs/SCOPE.md) | What's in / out of v1.0, FR/NFR, CLI + HTTP surface, decisions log |
241
+ | [`docs/ARCHITECTURE.md`](./docs/ARCHITECTURE.md) | Component diagram, sequence diagrams, `pw-emit` contract, HTTP API contract, error model |
242
+ | [`docs/PHASE_0_PLAN.md`](./docs/PHASE_0_PLAN.md) | The pw-emit extraction plan (mostly historical now — it shipped) |
243
+ | [`docs/STATUS.md`](./docs/STATUS.md) | Verified state right now — what works, what's pending, what's deferred |
244
+ | [`CHANGELOG.md`](./CHANGELOG.md) | Full version history |
245
+ | [`CONTRIBUTING.md`](./CONTRIBUTING.md) | Local dev, branching, PR conventions |
246
+
247
+ ## Examples
248
+
249
+ - [`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.
250
+ - [`examples/cms-login/`](./examples/cms-login/) — third-person dialect, compound input, `is redirected`. From a real OSS Selenium codebase.
251
+
252
+ ## License
253
+
254
+ MIT
package/dist/cli.d.ts ADDED
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Commander CLI entry. Thin shell over `src/index.ts`.
4
+ * See docs/SCOPE.md §8.
5
+ */
6
+ export {};