@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
@@ -0,0 +1,283 @@
1
+ "use strict";
2
+ /**
3
+ * Locator picker — given an `ElementIR` and the full snapshot context,
4
+ * choose the most stable Playwright locator API.
5
+ *
6
+ * Priority chain (FR-6):
7
+ * getByRole > getByLabel > getByPlaceholder > getByTestId > getByText
8
+ * > locator(css) > locator(xpath)
9
+ *
10
+ * The output `LocatorChoice` is then handed to `@vijaypjavvadi/pw-emit`
11
+ * which renders it into TS. We never render here.
12
+ */
13
+ Object.defineProperty(exports, "__esModule", { value: true });
14
+ exports.pickLocator = pickLocator;
15
+ exports.dedupeLocators = dedupeLocators;
16
+ const naming_1 = require("../utils/naming");
17
+ /**
18
+ * Pick the highest-priority unique locator for `element`. Uniqueness is
19
+ * checked against `allElements` — if multiple elements would match the
20
+ * candidate locator, we fall down the chain.
21
+ */
22
+ function pickLocator(element, allElements) {
23
+ const fieldName = synthFieldName(element);
24
+ // 1. getByRole + accessible name — most stable
25
+ if (element.role && element.name) {
26
+ const rivals = allElements.filter((e) => e !== element && e.role === element.role && e.name === element.name);
27
+ return {
28
+ api: "getByRole",
29
+ args: `${JSON.stringify(element.role)}, { name: ${JSON.stringify(element.name)} }`,
30
+ fieldName,
31
+ source: element,
32
+ confidence: rivals.length === 0 ? "unique" : "ambiguous",
33
+ };
34
+ }
35
+ // 2. getByLabel — for form inputs with associated <label>
36
+ if (element.label) {
37
+ const rivals = allElements.filter((e) => e !== element && e.label === element.label);
38
+ return {
39
+ api: "getByLabel",
40
+ args: JSON.stringify(element.label),
41
+ fieldName,
42
+ source: element,
43
+ confidence: rivals.length === 0 ? "unique" : "ambiguous",
44
+ };
45
+ }
46
+ // 3. getByPlaceholder
47
+ if (element.placeholder) {
48
+ const rivals = allElements.filter((e) => e !== element && e.placeholder === element.placeholder);
49
+ return {
50
+ api: "getByPlaceholder",
51
+ args: JSON.stringify(element.placeholder),
52
+ fieldName,
53
+ source: element,
54
+ confidence: rivals.length === 0 ? "unique" : "ambiguous",
55
+ };
56
+ }
57
+ // 4. getByTestId — when the app uses data-testid (best stability across releases)
58
+ if (element.testId) {
59
+ const rivals = allElements.filter((e) => e !== element && e.testId === element.testId);
60
+ return {
61
+ api: "getByTestId",
62
+ args: JSON.stringify(element.testId),
63
+ fieldName,
64
+ source: element,
65
+ confidence: rivals.length === 0 ? "unique" : "ambiguous",
66
+ };
67
+ }
68
+ // 5. getByText — last semantic choice.
69
+ // Skipped for status regions (error / alert / notification banners),
70
+ // where text content is volatile (changes per scenario) but the id
71
+ // is stable. Without this skip, `<div id="error">{current message}</div>`
72
+ // becomes `getByText("{current message}")` — which then breaks any
73
+ // assertion that expects a DIFFERENT error string.
74
+ const isStatusRegion = element.cssSelector !== undefined && STATUS_REGION_RE.test(element.cssSelector);
75
+ if (!isStatusRegion &&
76
+ element.text &&
77
+ element.text.trim().length > 0 &&
78
+ element.text.length < 80) {
79
+ const rivals = allElements.filter((e) => e !== element && e.text === element.text);
80
+ return {
81
+ api: "getByText",
82
+ args: JSON.stringify(element.text),
83
+ fieldName,
84
+ source: element,
85
+ confidence: rivals.length === 0 ? "unique" : "ambiguous",
86
+ };
87
+ }
88
+ // 6. CSS selector
89
+ if (element.cssSelector) {
90
+ return {
91
+ api: "locator",
92
+ args: JSON.stringify(element.cssSelector),
93
+ fieldName,
94
+ source: element,
95
+ confidence: "fallback",
96
+ };
97
+ }
98
+ // 7. xpath — last resort
99
+ if (element.xpath) {
100
+ return {
101
+ api: "locator",
102
+ args: JSON.stringify(`xpath=${element.xpath}`),
103
+ fieldName,
104
+ source: element,
105
+ confidence: "fallback",
106
+ };
107
+ }
108
+ // Nothing usable — emit a tag-only locator and flag as fallback
109
+ return {
110
+ api: "locator",
111
+ args: JSON.stringify(element.tag),
112
+ fieldName,
113
+ source: element,
114
+ confidence: "fallback",
115
+ };
116
+ }
117
+ /**
118
+ * Pattern for "status region" elements — error / alert / notification banners.
119
+ * When the CSS id (or class) matches this, we prefer the id-derived field
120
+ * name over the text-derived one, because:
121
+ * 1. The text content is incidental — `<div id="error">` shows different
122
+ * copy depending on what failed, but it's still "the error region".
123
+ * 2. Field names like `yourUsernameIsInvalid` (text-derived) defeat the
124
+ * step matcher's "I should see an error message" rule, which looks
125
+ * for fields with `Alert`/`Error`/`Message` suffixes.
126
+ * Naming a status region after its id (`error`, `errorMessage`,
127
+ * `notificationBanner`) is what a test author would write by hand.
128
+ */
129
+ const STATUS_REGION_RE = /(error|alert|message|warning|success|notification|status)/i;
130
+ /**
131
+ * Synthesise a camelCase TS field name for an element.
132
+ *
133
+ * Priority order for the *base name*:
134
+ * 0. CSS id when it matches STATUS_REGION_RE — special case so error
135
+ * banners get stable names regardless of their current text content.
136
+ * 1. element.name (accessible name)
137
+ * 2. element.label
138
+ * 3. element.placeholder
139
+ * 4. element.testId
140
+ * 5. element.text (first 40 chars)
141
+ * 6. CSS id from element.cssSelector (`#username` → `username`)
142
+ * 7. CSS class from element.cssSelector (`.foo-bar` → `fooBar`)
143
+ * 8. `<role|tag>Element` last resort
144
+ *
145
+ * Then appends a role-derived suffix if it's not already there:
146
+ * "username" + role:textbox → "usernameInput"
147
+ * "submit" + role:button → "submitButton"
148
+ */
149
+ function synthFieldName(element) {
150
+ // 0. Status region special case
151
+ const idDerived = cssSelectorToName(element.cssSelector);
152
+ if (idDerived && STATUS_REGION_RE.test(idDerived)) {
153
+ const cleaned = idDerived.replace(/[^a-zA-Z0-9 ]/g, " ").trim();
154
+ return (0, naming_1.camelCase)(cleaned) || "errorRegion";
155
+ }
156
+ const baseName = element.name ??
157
+ element.label ??
158
+ element.placeholder ??
159
+ element.testId ??
160
+ element.text?.slice(0, 40) ??
161
+ idDerived ??
162
+ `${element.role ?? element.tag}Element`;
163
+ const cleaned = baseName.replace(/[^a-zA-Z0-9 ]/g, " ").trim();
164
+ let fieldName = (0, naming_1.camelCase)(cleaned) || "element";
165
+ const suffix = roleToSuffix(element.role, element.tag);
166
+ if (suffix && !fieldName.toLowerCase().endsWith(suffix.toLowerCase())) {
167
+ fieldName += suffix;
168
+ }
169
+ return fieldName;
170
+ }
171
+ /**
172
+ * Best-effort name extraction from a CSS selector. Handles common shapes:
173
+ * #username → "username"
174
+ * .foo-bar → "foo bar"
175
+ * div#main → "main"
176
+ * a.wp-block-btn → "wp block btn"
177
+ * Returns undefined for selectors with no usable id/class.
178
+ */
179
+ function cssSelectorToName(selector) {
180
+ if (!selector)
181
+ return undefined;
182
+ // Prefer #id over .class
183
+ const idMatch = selector.match(/#([a-zA-Z][\w-]*)/);
184
+ if (idMatch)
185
+ return idMatch[1].replace(/-/g, " ");
186
+ const classMatch = selector.match(/\.([a-zA-Z][\w-]*)/);
187
+ if (classMatch)
188
+ return classMatch[1].replace(/-/g, " ");
189
+ return undefined;
190
+ }
191
+ function roleToSuffix(role, tag) {
192
+ switch (role) {
193
+ case "button":
194
+ return "Button";
195
+ case "link":
196
+ return "Link";
197
+ case "textbox":
198
+ case "searchbox":
199
+ return "Input";
200
+ case "checkbox":
201
+ return "Checkbox";
202
+ case "radio":
203
+ return "Radio";
204
+ case "combobox":
205
+ case "listbox":
206
+ return "Select";
207
+ case "heading":
208
+ return "Heading";
209
+ case "alert":
210
+ return "Alert";
211
+ case "img":
212
+ return "Image";
213
+ default:
214
+ break;
215
+ }
216
+ switch (tag) {
217
+ case "input":
218
+ return "Input";
219
+ case "select":
220
+ return "Select";
221
+ case "textarea":
222
+ return "Textarea";
223
+ case "a":
224
+ return "Link";
225
+ case "button":
226
+ return "Button";
227
+ default:
228
+ return "";
229
+ }
230
+ }
231
+ /**
232
+ * Dedupe a list of locator choices.
233
+ *
234
+ * 1. **By locator identity (api + args).** When two `LocatorChoice`s
235
+ * resolve to the same Playwright call (e.g. a `<label>` and the
236
+ * `<input>` it labels both produce `getByLabel('Username')`), keep
237
+ * only the FIRST. Without this, the live scanner emits
238
+ * `username = page.getByLabel('Username')` AND
239
+ * `usernameInput = page.getByLabel('Username')` — two fields, one
240
+ * element, confusing for tests and noisy in the POM.
241
+ * The kept entry uses the LONGER fieldName when names differ —
242
+ * `usernameInput` is more useful than `username` because the
243
+ * step-matcher's preferredSuffix logic looks for `*Input`.
244
+ *
245
+ * 2. **By field name collision.** If two elements legitimately deserve
246
+ * different `LocatorChoice`s but happen to synthesise the same
247
+ * fieldName, suffix the later one with `2`, `3`, … so emitted TS
248
+ * stays valid.
249
+ *
250
+ * Stable-sorted by input order otherwise.
251
+ */
252
+ function dedupeLocators(choices) {
253
+ // Pass 1: collapse by locator identity. Picks the better field name.
254
+ const byLocator = new Map();
255
+ for (const c of choices) {
256
+ const key = `${c.api}::${c.args}`;
257
+ const existing = byLocator.get(key);
258
+ if (!existing) {
259
+ byLocator.set(key, c);
260
+ continue;
261
+ }
262
+ // Prefer the longer / more descriptive field name (usernameInput > username)
263
+ if (c.fieldName.length > existing.fieldName.length) {
264
+ byLocator.set(key, c);
265
+ }
266
+ }
267
+ const collapsed = Array.from(byLocator.values());
268
+ // Pass 2: resolve any remaining fieldName collisions with a numeric suffix.
269
+ const seen = new Map();
270
+ const out = [];
271
+ for (const c of collapsed) {
272
+ const count = seen.get(c.fieldName) ?? 0;
273
+ seen.set(c.fieldName, count + 1);
274
+ if (count === 0) {
275
+ out.push(c);
276
+ }
277
+ else {
278
+ out.push({ ...c, fieldName: `${c.fieldName}${count + 1}` });
279
+ }
280
+ }
281
+ return out;
282
+ }
283
+ //# sourceMappingURL=locatorPicker.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"locatorPicker.js","sourceRoot":"","sources":["../../src/transformers/locatorPicker.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,28 @@
1
+ /**
2
+ * POM resolver — given a requested page name and the existing POM map from
3
+ * the repo scanner, decide reuse / augment / create. See docs/ARCHITECTURE.md §2.
4
+ *
5
+ * Decision table (referenced fields = field names the matched steps need):
6
+ * ┌────────────┬──────────────────────────────┬──────────┐
7
+ * │ POM exists │ all referenced fields present│ decision │
8
+ * ├────────────┼──────────────────────────────┼──────────┤
9
+ * │ no │ n/a │ CREATE │
10
+ * │ yes │ yes │ REUSE │
11
+ * │ yes │ no │ AUGMENT │
12
+ * └────────────┴──────────────────────────────┴──────────┘
13
+ */
14
+ import type { PageObjectIR } from "../types";
15
+ export type PomDecision = "REUSE" | "AUGMENT" | "CREATE";
16
+ export interface ResolvePomInput {
17
+ requestedName: string;
18
+ existing: Map<string, PageObjectIR>;
19
+ /** Element field names referenced by the matched steps. */
20
+ referencedFields: string[];
21
+ }
22
+ export interface ResolvePomResult {
23
+ decision: PomDecision;
24
+ existing?: PageObjectIR;
25
+ /** Field names referenced but not yet present on the existing POM. */
26
+ missingFields: string[];
27
+ }
28
+ export declare function resolvePom(input: ResolvePomInput): ResolvePomResult;
@@ -0,0 +1,35 @@
1
+ "use strict";
2
+ /**
3
+ * POM resolver — given a requested page name and the existing POM map from
4
+ * the repo scanner, decide reuse / augment / create. See docs/ARCHITECTURE.md §2.
5
+ *
6
+ * Decision table (referenced fields = field names the matched steps need):
7
+ * ┌────────────┬──────────────────────────────┬──────────┐
8
+ * │ POM exists │ all referenced fields present│ decision │
9
+ * ├────────────┼──────────────────────────────┼──────────┤
10
+ * │ no │ n/a │ CREATE │
11
+ * │ yes │ yes │ REUSE │
12
+ * │ yes │ no │ AUGMENT │
13
+ * └────────────┴──────────────────────────────┴──────────┘
14
+ */
15
+ Object.defineProperty(exports, "__esModule", { value: true });
16
+ exports.resolvePom = resolvePom;
17
+ function resolvePom(input) {
18
+ const { requestedName, existing, referencedFields } = input;
19
+ const found = existing.get(requestedName);
20
+ if (!found) {
21
+ return {
22
+ decision: "CREATE",
23
+ missingFields: [...new Set(referencedFields)],
24
+ };
25
+ }
26
+ const presentFieldNames = new Set(found.fields.map((f) => f.fieldName));
27
+ const missingFields = [
28
+ ...new Set(referencedFields.filter((f) => !presentFieldNames.has(f))),
29
+ ];
30
+ if (missingFields.length === 0) {
31
+ return { decision: "REUSE", existing: found, missingFields: [] };
32
+ }
33
+ return { decision: "AUGMENT", existing: found, missingFields };
34
+ }
35
+ //# sourceMappingURL=pomResolver.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pomResolver.js","sourceRoot":"","sources":["../../src/transformers/pomResolver.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;GAYG;;AAoBH,gCAqBC;AArBD,SAAgB,UAAU,CAAC,KAAsB;IAC/C,MAAM,EAAE,aAAa,EAAE,QAAQ,EAAE,gBAAgB,EAAE,GAAG,KAAK,CAAC;IAC5D,MAAM,KAAK,GAAG,QAAQ,CAAC,GAAG,CAAC,aAAa,CAAC,CAAC;IAE1C,IAAI,CAAC,KAAK,EAAE,CAAC;QACX,OAAO;YACL,QAAQ,EAAE,QAAQ;YAClB,aAAa,EAAE,CAAC,GAAG,IAAI,GAAG,CAAC,gBAAgB,CAAC,CAAC;SAC9C,CAAC;IACJ,CAAC;IAED,MAAM,iBAAiB,GAAG,IAAI,GAAG,CAAC,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC;IACxE,MAAM,aAAa,GAAG;QACpB,GAAG,IAAI,GAAG,CAAC,gBAAgB,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,iBAAiB,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;KACtE,CAAC;IAEF,IAAI,aAAa,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC/B,OAAO,EAAE,QAAQ,EAAE,OAAO,EAAE,QAAQ,EAAE,KAAK,EAAE,aAAa,EAAE,EAAE,EAAE,CAAC;IACnE,CAAC;IAED,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,QAAQ,EAAE,KAAK,EAAE,aAAa,EAAE,CAAC;AACjE,CAAC"}
@@ -0,0 +1,36 @@
1
+ /**
2
+ * Step matcher — Gherkin step text → POM method call / expect() assertion.
3
+ *
4
+ * Phase 1: rule-based only. The rule table covers the standard verb set:
5
+ * - navigation: "I am on / navigate to / open / visit / go to"
6
+ * - input: "I enter / type / fill X (in)to <field>"
7
+ * - selection: "I select <option> from <select>"
8
+ * - clicking: "I click / press / tap <button>"
9
+ * - check/uncheck: "I check / uncheck <checkbox>"
10
+ * - visibility: "I (should) see / I should NOT see"
11
+ * - text equals: "I should see <X> in <field>" / "<field> should contain <X>"
12
+ * - URL: "the URL should be / contain"
13
+ *
14
+ * LLM fallback for unmatched steps is wired but defaults to off; falls
15
+ * through to a `// TODO` warning. See docs/ARCHITECTURE.md §2 (LLM branch).
16
+ */
17
+ import type { PageObjectIR, ScenarioIR, StepBinding, StepIR } from "../types";
18
+ export interface MatchStepsInput {
19
+ scenarios: ScenarioIR[];
20
+ pom: PageObjectIR;
21
+ /** camelCase variable name for the POM in the spec (e.g. `loginPage`). */
22
+ pageVar: string;
23
+ llmProvider?: "anthropic" | "openai" | "gemini";
24
+ governanceUrl?: string;
25
+ }
26
+ /**
27
+ * Match every step in every scenario to a POM call / assertion / TODO.
28
+ * Background steps are also threaded through if the orchestrator passes
29
+ * them as a synthetic scenario.
30
+ */
31
+ export declare function matchSteps(input: MatchStepsInput): Promise<StepBinding[][]>;
32
+ /**
33
+ * Try every rule in priority order; return the first match.
34
+ * Falls through to a warning binding when nothing matches.
35
+ */
36
+ export declare function matchStep(step: StepIR, pom: PageObjectIR, pageVar: string): StepBinding;