@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,461 @@
1
+ "use strict";
2
+ /**
3
+ * Step matcher — Gherkin step text → POM method call / expect() assertion.
4
+ *
5
+ * Phase 1: rule-based only. The rule table covers the standard verb set:
6
+ * - navigation: "I am on / navigate to / open / visit / go to"
7
+ * - input: "I enter / type / fill X (in)to <field>"
8
+ * - selection: "I select <option> from <select>"
9
+ * - clicking: "I click / press / tap <button>"
10
+ * - check/uncheck: "I check / uncheck <checkbox>"
11
+ * - visibility: "I (should) see / I should NOT see"
12
+ * - text equals: "I should see <X> in <field>" / "<field> should contain <X>"
13
+ * - URL: "the URL should be / contain"
14
+ *
15
+ * LLM fallback for unmatched steps is wired but defaults to off; falls
16
+ * through to a `// TODO` warning. See docs/ARCHITECTURE.md §2 (LLM branch).
17
+ */
18
+ Object.defineProperty(exports, "__esModule", { value: true });
19
+ exports.matchSteps = matchSteps;
20
+ exports.matchStep = matchStep;
21
+ const naming_1 = require("../utils/naming");
22
+ /**
23
+ * Match every step in every scenario to a POM call / assertion / TODO.
24
+ * Background steps are also threaded through if the orchestrator passes
25
+ * them as a synthetic scenario.
26
+ */
27
+ async function matchSteps(input) {
28
+ const out = [];
29
+ for (const scenario of input.scenarios) {
30
+ out.push(scenario.steps.map((step) => matchStep(step, input.pom, input.pageVar)));
31
+ }
32
+ return out;
33
+ }
34
+ /**
35
+ * Try every rule in priority order; return the first match.
36
+ * Falls through to a warning binding when nothing matches.
37
+ */
38
+ function matchStep(step, pom, pageVar) {
39
+ for (const rule of RULES) {
40
+ const m = step.text.match(rule.pattern);
41
+ if (m) {
42
+ const result = rule.build(m, step, pom, pageVar);
43
+ if (result)
44
+ return result;
45
+ }
46
+ }
47
+ return {
48
+ step,
49
+ warning: `no rule matched: "${step.keyword} ${step.text}"`,
50
+ };
51
+ }
52
+ // --- Rule registry ---------------------------------------------------------
53
+ /**
54
+ * Subject prefix accepted at the start of a step. Real-world Cucumber suites
55
+ * mix several conventions:
56
+ * "I click the login button" — first-person (most common)
57
+ * "user clicks the login button" — third-person (selenium14, etc.)
58
+ * "User clicks the login button" — capitalised
59
+ * "the user clicks the login button" — definite article
60
+ *
61
+ * All rules use this prefix instead of a hard-coded `^I ` so the same rule
62
+ * matches all four shapes. Verbs are normalised on the rule side (no need
63
+ * to also accept "clicks" + "click" — the regex below has both).
64
+ */
65
+ const SUBJ = "(?:I|user|User|the user|the User)";
66
+ const RULES = [
67
+ // 1. Navigation: "I am on the login page" / "I navigate to /foo"
68
+ {
69
+ pattern: new RegExp(`^(?:${SUBJ} )?(?:am on|navigate to|navigates? to|open|opens?|visit|visits?|go to|goes? to|am at|is at) (?:the )?(?:.+?\\s+)?["']?([^"']+?)["']?(?: page)?$`, "i"),
70
+ build: (m, step, pom, pageVar) => {
71
+ // The non-greedy `(?:.+?\s+)?` before the captured target lets us
72
+ // handle both shapes:
73
+ // "I navigate to the login page" → m[1] = "login"
74
+ // "User navigate to Login Page for Admin \"https://x.com/login\"" → m[1] = "https://x.com/login"
75
+ const target = m[1].trim();
76
+ const hasGoto = pom.methods.some((mm) => mm.name === "goto");
77
+ if (hasGoto) {
78
+ return {
79
+ step,
80
+ pomCall: { page: pageVar, method: "goto", args: [] },
81
+ };
82
+ }
83
+ return {
84
+ step,
85
+ pomCall: { page: pageVar, method: "goto", args: [JSON.stringify(target)] },
86
+ };
87
+ },
88
+ },
89
+ // 2a. Input with explicit value + field: 'I enter "alice" in(to) the username field'
90
+ {
91
+ pattern: new RegExp(`^${SUBJ} (?:enter|enters|type|types|fill|fills) ["']([^"']*)["'] (?:in|into|in to) (?:the )?(.+?)(?: field| input| box)?$`, "i"),
92
+ build: (m, step, pom, pageVar) => fillFieldBinding(step, pom, pageVar, m[2].trim(), m[1]),
93
+ },
94
+ // 2c. Compound input: 'user enter email "X" password "Y"'.
95
+ // Common in real-world Cucumber suites (e.g. selenium14/LoginCMS).
96
+ // Emits a `customBody` with one fill() per (field, value) pair.
97
+ // Pattern: SUBJ verb FIELD1 "VAL1" FIELD2 "VAL2" [FIELD3 "VAL3" ...]
98
+ // The `(?:and )?` between pairs is optional ("email X password Y" or
99
+ // "email X and password Y").
100
+ {
101
+ pattern: new RegExp(`^${SUBJ} (?:enter|enters|type|types|fill|fills) (?:(?:the )?(\\w+) ["']([^"']*)["'](?: and| and the| ,)?\\s*){2,}$`, "i"),
102
+ build: (_m, step, pom, pageVar) => {
103
+ // Re-extract all (field, value) pairs from the step text — the regex
104
+ // above only captures the LAST match in the iteration, so we need a
105
+ // second pass with `g` flag to get all of them.
106
+ const pairRe = /(?:the )?(\w+)\s+["']([^"']*)["']/g;
107
+ const pairs = [];
108
+ let pm;
109
+ // Strip the leading subject + verb to avoid false matches
110
+ const tail = step.text.replace(new RegExp(`^${SUBJ} (?:enter|enters|type|types|fill|fills) `, "i"), "");
111
+ while ((pm = pairRe.exec(tail)) !== null) {
112
+ pairs.push({ field: pm[1], value: pm[2] });
113
+ }
114
+ if (pairs.length < 2)
115
+ return null;
116
+ const lines = [];
117
+ for (const { field, value } of pairs) {
118
+ const f = findField(pom, field, ["Input", "Field", "Box", "Textbox"]);
119
+ if (!f) {
120
+ // Couldn't resolve one of the fields — bail out so the step
121
+ // becomes a TODO instead of a half-correct emission.
122
+ return null;
123
+ }
124
+ lines.push(`await ${pageVar}.${f.fieldName}.fill(${JSON.stringify(value)});`);
125
+ }
126
+ return { step, customBody: lines.join("\n") };
127
+ },
128
+ },
129
+ // 2b. Reversed word order: 'I enter username "alice"' / 'I type password "secret"'
130
+ // Common in real-world Cucumber suites (e.g. test/test/login.feature).
131
+ // Also handles empty values: `I enter username ""`.
132
+ {
133
+ pattern: new RegExp(`^${SUBJ} (?:enter|enters|type|types|fill|fills) (?:the )?(.+?) ["']([^"']*)["']$`, "i"),
134
+ build: (m, step, pom, pageVar) => {
135
+ // If the captured "field" is itself a quoted value already handled by 2a,
136
+ // skip — but 2a's pattern wouldn't end here so 2b is safe.
137
+ return fillFieldBinding(step, pom, pageVar, m[1].trim(), m[2]);
138
+ },
139
+ },
140
+ // 3. Click: "I click the login button" / "I press 'Sign in'"
141
+ // Captures the explicit role suffix (button/link/icon) so findField
142
+ // can restrict to fields with the matching suffix. Without this,
143
+ // "click the login button" would match `testLoginHeading` via the
144
+ // generic substring fallback.
145
+ {
146
+ pattern: new RegExp(`^(?:${SUBJ} )?(?:click|clicks|press|presses|tap|taps)(?: on)? (?:the )?["']?([^"']+?)["']?(?:\\s+(button|link|icon|tab))?$`, "i"),
147
+ build: (m, step, pom, pageVar) => {
148
+ const hint = m[1].trim();
149
+ const explicitRole = m[2]; // 'button' | 'link' | 'icon' | 'tab' | undefined
150
+ const suffixes = explicitRole
151
+ ? [capitalize(explicitRole)]
152
+ : ["Button", "Link"];
153
+ const field = findField(pom, hint, suffixes);
154
+ if (!field)
155
+ return null;
156
+ const method = findOrSynthMethod(pom, `click${capitalize(field.fieldName)}`, []);
157
+ if (method && method.kind === "existing") {
158
+ return { step, pomCall: { page: pageVar, method: method.name, args: [] } };
159
+ }
160
+ return {
161
+ step,
162
+ pomCall: { page: pageVar, method: `${field.fieldName}.click`, args: [] },
163
+ };
164
+ },
165
+ },
166
+ // 4. Selection: 'I select "USA" from (the) country dropdown'
167
+ {
168
+ pattern: new RegExp(`^${SUBJ} (?:select|selects) ["']([^"']+)["'] from (?:the )?(.+?)(?: dropdown| select)?$`, "i"),
169
+ build: (m, step, pom, pageVar) => {
170
+ const value = m[1];
171
+ const hint = m[2].trim();
172
+ const field = findField(pom, hint, ["Select", "Dropdown"]);
173
+ if (!field)
174
+ return null;
175
+ return {
176
+ step,
177
+ pomCall: {
178
+ page: pageVar,
179
+ method: `${field.fieldName}.selectOption`,
180
+ args: [JSON.stringify(value)],
181
+ },
182
+ };
183
+ },
184
+ },
185
+ // 5. Check / uncheck checkbox
186
+ {
187
+ pattern: new RegExp(`^${SUBJ} (check|checks|uncheck|unchecks) (?:the )?(.+?)(?: checkbox)?$`, "i"),
188
+ build: (m, step, pom, pageVar) => {
189
+ const verb = m[1].toLowerCase() === "check" ? "check" : "uncheck";
190
+ const hint = m[2].trim();
191
+ const field = findField(pom, hint, ["Checkbox", "Toggle"]);
192
+ if (!field)
193
+ return null;
194
+ return {
195
+ step,
196
+ pomCall: { page: pageVar, method: `${field.fieldName}.${verb}`, args: [] },
197
+ };
198
+ },
199
+ },
200
+ // 6. Visibility: "I (should) see <X>" — assertion
201
+ {
202
+ pattern: new RegExp(`^${SUBJ} (?:should )?(?:see|sees) (?:the )?["']?([^"']+?)["']?$`, "i"),
203
+ build: (m, step, pom, pageVar) => {
204
+ const hint = m[1].trim();
205
+ const field = findField(pom, hint, []);
206
+ const locatorExpr = field
207
+ ? `${pageVar}.${field.fieldName}`
208
+ : `${pageVar}.page.getByText(${JSON.stringify(hint)})`;
209
+ return {
210
+ step,
211
+ assertion: { locator: locatorExpr, matcher: "toBeVisible" },
212
+ };
213
+ },
214
+ },
215
+ // 7. Negative visibility: "I should NOT see <X>"
216
+ {
217
+ pattern: new RegExp(`^${SUBJ} should not see (?:the )?["']?([^"']+?)["']?$`, "i"),
218
+ build: (m, step, pom, pageVar) => {
219
+ const hint = m[1].trim();
220
+ const field = findField(pom, hint, []);
221
+ const locatorExpr = field
222
+ ? `${pageVar}.${field.fieldName}`
223
+ : `${pageVar}.page.getByText(${JSON.stringify(hint)})`;
224
+ return {
225
+ step,
226
+ assertion: { locator: locatorExpr, matcher: "toBeHidden" },
227
+ };
228
+ },
229
+ },
230
+ // 8. Error message catch-all (must run BEFORE the generic text-equality
231
+ // rule, otherwise rule 9 below would consume "I should see the/an error
232
+ // message X" with a toHaveText matcher and a non-unique getByText fallback.
233
+ // Accepts both "the error message" and "an error message" wordings.
234
+ // The locator preference order goes Alert > Error > Message — when the
235
+ // page has a dedicated alert/error field, use it instead of generic text.
236
+ {
237
+ pattern: new RegExp(`^${SUBJ} should see (?:the|an|a) error(?: message)? ["']([^"']+)["']$`, "i"),
238
+ build: (m, step, pom, pageVar) => {
239
+ const expected = m[1];
240
+ const field = findField(pom, "errorMessage", ["Alert", "Error", "Message"]) ??
241
+ findField(pom, "error", ["Alert", "Error"]);
242
+ const locatorExpr = field
243
+ ? `${pageVar}.${field.fieldName}`
244
+ : `${pageVar}.page.getByText(${JSON.stringify(expected)})`;
245
+ return {
246
+ step,
247
+ assertion: {
248
+ locator: locatorExpr,
249
+ matcher: "toContainText",
250
+ expected: JSON.stringify(expected),
251
+ },
252
+ };
253
+ },
254
+ },
255
+ // 9a. Text "containing" / "with text" — substring match.
256
+ // MUST run before rule 9b (exact text), otherwise:
257
+ // "I should see a welcome message containing 'Congratulations'"
258
+ // would match rule 9b with hint="a welcome message containing" and
259
+ // emit toHaveText("Congratulations") — which fails when the actual
260
+ // element text is "Congratulations student. You successfully logged in!".
261
+ {
262
+ pattern: new RegExp(`^${SUBJ} should see (?:the |a |an )?(.+?) (?:containing|that contains?|with(?: the)? text|having text) ["']([^"']+)["']$`, "i"),
263
+ build: (m, step, pom, pageVar) => {
264
+ const hint = m[1].trim();
265
+ const expected = m[2];
266
+ const field = findField(pom, hint, ["Message", "Heading", "Text", "Label"]);
267
+ const locatorExpr = field
268
+ ? `${pageVar}.${field.fieldName}`
269
+ : `${pageVar}.page.getByText(${JSON.stringify(expected)})`;
270
+ return {
271
+ step,
272
+ assertion: {
273
+ locator: locatorExpr,
274
+ matcher: "toContainText",
275
+ expected: JSON.stringify(expected),
276
+ },
277
+ };
278
+ },
279
+ },
280
+ // 9b. Text equality: 'I should see the welcome message "Welcome, Alice"'
281
+ // Strict toHaveText — the entire element text must equal the expected
282
+ // value. Use rule 9a above for substring semantics.
283
+ {
284
+ pattern: new RegExp(`^${SUBJ} should see (?:the |a |an )?(.+?) ["']([^"']+)["']$`, "i"),
285
+ build: (m, step, pom, pageVar) => {
286
+ const hint = m[1].trim();
287
+ const expected = m[2];
288
+ const field = findField(pom, hint, ["Message", "Heading", "Text", "Label"]);
289
+ const locatorExpr = field
290
+ ? `${pageVar}.${field.fieldName}`
291
+ : `${pageVar}.page.getByText(${JSON.stringify(expected)})`;
292
+ return {
293
+ step,
294
+ assertion: {
295
+ locator: locatorExpr,
296
+ matcher: "toHaveText",
297
+ expected: JSON.stringify(expected),
298
+ },
299
+ };
300
+ },
301
+ },
302
+ // 10. Remain on page: "I should remain on the login page"
303
+ {
304
+ pattern: new RegExp(`^${SUBJ} should remain on (?:the )?(.+?)(?: page)?$`, "i"),
305
+ build: (m, step, _pom, pageVar) => {
306
+ const target = m[1].trim();
307
+ return {
308
+ step,
309
+ assertion: {
310
+ locator: `${pageVar}.page`,
311
+ matcher: "toHaveURL",
312
+ expected: `new RegExp(${JSON.stringify(target.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"))})`,
313
+ },
314
+ };
315
+ },
316
+ },
317
+ // 11. Redirect: "I should be redirected to the logged-in page" / "to /dashboard"
318
+ // Asserts URL changed away from the previous page. Best-effort: regex matches
319
+ // the target token in the URL.
320
+ {
321
+ pattern: new RegExp(`^${SUBJ} (?:should be |am |is )?redirected to (?:the )?(.+?)(?: page)?$`, "i"),
322
+ build: (m, step, _pom, pageVar) => {
323
+ const target = m[1].trim();
324
+ const slug = target.replace(/\s+/g, "[-_/]?");
325
+ return {
326
+ step,
327
+ assertion: {
328
+ locator: `${pageVar}.page`,
329
+ matcher: "toHaveURL",
330
+ expected: `new RegExp(${JSON.stringify(slug)})`,
331
+ },
332
+ };
333
+ },
334
+ },
335
+ // 12. Attribute check: 'the password field should be of type "password"'
336
+ {
337
+ pattern: /^the (.+?) field should (?:be of type|have type) ["']([^"']+)["']$/i,
338
+ build: (m, step, pom, pageVar) => {
339
+ const fieldHint = m[1].trim();
340
+ const expected = m[2];
341
+ const field = findField(pom, fieldHint, ["Input", "Field", "Box", "Textbox"]);
342
+ const locatorExpr = field
343
+ ? `${pageVar}.${field.fieldName}`
344
+ : `${pageVar}.page.locator(${JSON.stringify(`[name="${fieldHint}"]`)})`;
345
+ return {
346
+ step,
347
+ assertion: {
348
+ locator: locatorExpr,
349
+ matcher: "toHaveAttribute",
350
+ expected: `"type", ${JSON.stringify(expected)}`,
351
+ },
352
+ };
353
+ },
354
+ },
355
+ // 13. URL prefix/contains: 'the current URL should start with "https"'
356
+ {
357
+ pattern: /^the (?:current )?URL should (?:start with|begin with|contain|include) ["']([^"']+)["']$/i,
358
+ build: (m, step, _pom, pageVar) => {
359
+ const expected = m[1];
360
+ // start-with → anchored regex; contain → unanchored
361
+ const verb = step.text.toLowerCase();
362
+ const isPrefix = verb.includes("start with") || verb.includes("begin with");
363
+ const escaped = expected.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
364
+ return {
365
+ step,
366
+ assertion: {
367
+ locator: `${pageVar}.page`,
368
+ matcher: "toHaveURL",
369
+ expected: `new RegExp(${JSON.stringify(isPrefix ? "^" + escaped : escaped)})`,
370
+ },
371
+ };
372
+ },
373
+ },
374
+ ];
375
+ /**
376
+ * Shared helper for both word orders of the input-into-field rule.
377
+ * Picks an existing `fill<Field>` method if defined, else uses a direct locator call.
378
+ */
379
+ function fillFieldBinding(step, pom, pageVar, fieldHint, value) {
380
+ const field = findField(pom, fieldHint, ["Input", "Field", "Box", "Textbox"]);
381
+ if (!field)
382
+ return null;
383
+ const method = findOrSynthMethod(pom, `fill${capitalize(field.fieldName)}`, [
384
+ { name: "v", type: "string" },
385
+ ]);
386
+ if (method && method.kind === "existing") {
387
+ return {
388
+ step,
389
+ pomCall: { page: pageVar, method: method.name, args: [JSON.stringify(value)] },
390
+ };
391
+ }
392
+ return {
393
+ step,
394
+ pomCall: {
395
+ page: pageVar,
396
+ method: `${field.fieldName}.fill`,
397
+ args: [JSON.stringify(value)],
398
+ },
399
+ };
400
+ }
401
+ // --- Helpers ---------------------------------------------------------------
402
+ /**
403
+ * Find a POM field whose name reasonably matches `hint`.
404
+ *
405
+ * Resolution order:
406
+ * 1. Exact case-insensitive match.
407
+ * 2. norm + each preferred suffix (so "login" + ["Button"] → "loginButton").
408
+ * 3. If preferredSuffixes is non-empty: ONLY consider fields whose name
409
+ * ends with one of those suffixes. Within that subset:
410
+ * a. Substring match (hint inside fieldName, or vice versa)
411
+ * b. If exactly one field matches the suffix at all, return it
412
+ * (e.g. "I click the login button" with one *Button on the page →
413
+ * that button, regardless of name)
414
+ * 4. Generic substring match across all fields (last resort).
415
+ *
416
+ * Fixes the bug where "login button" matched `testLoginHeading` because
417
+ * "login" is a substring of "testloginheading" — even though there was a
418
+ * perfectly good `submitButton` on the same page.
419
+ */
420
+ function findField(pom, hint, preferredSuffixes) {
421
+ const norm = (0, naming_1.camelCase)(hint.replace(/[^a-zA-Z0-9 ]/g, ""));
422
+ const lower = norm.toLowerCase();
423
+ // 1. Exact match
424
+ let f = pom.fields.find((x) => x.fieldName.toLowerCase() === lower);
425
+ if (f)
426
+ return f;
427
+ // 2. norm + each preferred suffix
428
+ for (const suf of preferredSuffixes) {
429
+ const target = (norm + suf).toLowerCase();
430
+ f = pom.fields.find((x) => x.fieldName.toLowerCase() === target);
431
+ if (f)
432
+ return f;
433
+ }
434
+ // 3. Suffix-constrained matching — prefer same-role fields
435
+ if (preferredSuffixes.length > 0) {
436
+ const suffixMatched = pom.fields.filter((x) => preferredSuffixes.some((s) => x.fieldName.toLowerCase().endsWith(s.toLowerCase())));
437
+ // 3a. substring match within the suffix-matched subset
438
+ f = suffixMatched.find((x) => x.fieldName.toLowerCase().includes(lower) || lower.includes(x.fieldName.toLowerCase()));
439
+ if (f)
440
+ return f;
441
+ // 3b. exactly one suffix-matched field on the whole page → use it
442
+ if (suffixMatched.length === 1)
443
+ return suffixMatched[0];
444
+ }
445
+ // 4. Generic substring match (last resort, may hit cross-role fields)
446
+ f = pom.fields.find((x) => x.fieldName.toLowerCase().includes(lower));
447
+ if (f)
448
+ return f;
449
+ f = pom.fields.find((x) => lower.includes(x.fieldName.toLowerCase()));
450
+ return f;
451
+ }
452
+ function findOrSynthMethod(pom, desiredName, _params) {
453
+ const existing = pom.methods.find((m) => m.name === desiredName);
454
+ if (existing)
455
+ return { kind: "existing", name: existing.name };
456
+ return { kind: "synth", name: desiredName };
457
+ }
458
+ function capitalize(s) {
459
+ return s.charAt(0).toUpperCase() + s.slice(1);
460
+ }
461
+ //# sourceMappingURL=stepMatcher.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stepMatcher.js","sourceRoot":"","sources":["../../src/transformers/stepMatcher.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,166 @@
1
+ /**
2
+ * Intermediate representation (IR) shared across parser, transformers, and emitters.
3
+ * See docs/SCOPE.md §13 for the canonical shape.
4
+ */
5
+ export interface FeatureIR {
6
+ name: string;
7
+ description?: string;
8
+ background?: StepIR[];
9
+ scenarios: ScenarioIR[];
10
+ tags: string[];
11
+ sourceFile: string;
12
+ }
13
+ export interface ScenarioIR {
14
+ name: string;
15
+ steps: StepIR[];
16
+ /** Populated for `Scenario Outline` — one row per `Examples:` entry. */
17
+ examples?: Record<string, string>[];
18
+ tags: string[];
19
+ }
20
+ export interface StepIR {
21
+ keyword: "Given" | "When" | "Then" | "And" | "But";
22
+ text: string;
23
+ /** Doc string or data table argument when present. */
24
+ argument?: string | string[][];
25
+ }
26
+ export interface ElementIR {
27
+ role?: string;
28
+ name?: string;
29
+ label?: string;
30
+ placeholder?: string;
31
+ testId?: string;
32
+ text?: string;
33
+ cssSelector?: string;
34
+ xpath?: string;
35
+ tag: string;
36
+ bounds?: {
37
+ x: number;
38
+ y: number;
39
+ w: number;
40
+ h: number;
41
+ };
42
+ }
43
+ export interface LocatorChoice {
44
+ api: "getByRole" | "getByLabel" | "getByPlaceholder" | "getByTestId" | "getByText" | "locator";
45
+ args: string;
46
+ /** camelCase TS field name (e.g. `submitButton`). */
47
+ fieldName: string;
48
+ source: ElementIR;
49
+ confidence: "unique" | "ambiguous" | "fallback";
50
+ }
51
+ export interface PageObjectIR {
52
+ className: string;
53
+ filePath: string;
54
+ url?: string;
55
+ fields: LocatorChoice[];
56
+ methods: PomMethodIR[];
57
+ /** True when the file already exists on disk and we're augmenting. */
58
+ exists: boolean;
59
+ }
60
+ export interface PomMethodIR {
61
+ name: string;
62
+ params: {
63
+ name: string;
64
+ type: string;
65
+ }[];
66
+ /** Pre-rendered TS body. */
67
+ body: string;
68
+ origin: "existing" | "generated";
69
+ }
70
+ export interface StepBinding {
71
+ step: StepIR;
72
+ pomCall?: {
73
+ page: string;
74
+ method: string;
75
+ args: string[];
76
+ };
77
+ assertion?: {
78
+ locator: string;
79
+ matcher: string;
80
+ expected?: string;
81
+ };
82
+ /**
83
+ * Pre-rendered body for compound steps (one Gherkin step → multiple TS
84
+ * statements). When present, the emitter writes `customBody` verbatim
85
+ * instead of synthesising lines from `pomCall` / `assertion`.
86
+ * Example: `When user enter email "x" password "y"` → two `.fill()` lines.
87
+ */
88
+ customBody?: string;
89
+ /** Populated when no clean mapping was found. */
90
+ warning?: string;
91
+ }
92
+ export interface ReviewItem {
93
+ severity: "info" | "warn" | "error";
94
+ file?: string;
95
+ line?: number;
96
+ message: string;
97
+ suggestion?: string;
98
+ }
99
+ export interface ScaffoldOptions {
100
+ feature: string;
101
+ url: string;
102
+ page: string;
103
+ repo: string;
104
+ pages?: string[];
105
+ storageState?: string;
106
+ headed?: boolean;
107
+ llm?: "anthropic" | "openai" | "gemini";
108
+ governanceUrl?: string;
109
+ templates?: string;
110
+ dryRun?: boolean;
111
+ noValidate?: boolean;
112
+ telemetry?: boolean;
113
+ force?: boolean;
114
+ /**
115
+ * Skip page discovery entirely — use an empty element list. Useful for
116
+ * rule-only probing (CI, offline analysis, "what does the matcher do
117
+ * with this feature?" investigations). Synthesised goto() still works
118
+ * because it uses `--url` directly. Field-referencing rules will fall
119
+ * to TODO since the POM has no fields.
120
+ */
121
+ noDiscovery?: boolean;
122
+ /**
123
+ * If set, read snapshot from this JSON file instead of launching a
124
+ * browser. Phase 1a fallback; also used by regression tests for
125
+ * offline runs.
126
+ */
127
+ snapshotFile?: string;
128
+ }
129
+ export interface ScaffoldResult {
130
+ filesWritten: string[];
131
+ reviewItems: ReviewItem[];
132
+ tscErrorCount: number;
133
+ reviewReportPath: string;
134
+ }
135
+ export interface AnalyzeOptions {
136
+ feature: string;
137
+ url: string;
138
+ storageState?: string;
139
+ headed?: boolean;
140
+ }
141
+ export interface AnalyzeResult {
142
+ feature: FeatureIR;
143
+ discoveredLocators: LocatorChoice[];
144
+ bindings: StepBinding[];
145
+ warnings: ReviewItem[];
146
+ }
147
+ export interface UpdatePomOptions {
148
+ page: string;
149
+ url: string;
150
+ repo: string;
151
+ storageState?: string;
152
+ headed?: boolean;
153
+ templates?: string;
154
+ }
155
+ export interface UpdatePomResult {
156
+ added: {
157
+ fields: number;
158
+ methods: number;
159
+ };
160
+ preserved: {
161
+ fields: number;
162
+ methods: number;
163
+ };
164
+ filePath: string;
165
+ reviewItems: ReviewItem[];
166
+ }
package/dist/types.js ADDED
@@ -0,0 +1,7 @@
1
+ "use strict";
2
+ /**
3
+ * Intermediate representation (IR) shared across parser, transformers, and emitters.
4
+ * See docs/SCOPE.md §13 for the canonical shape.
5
+ */
6
+ Object.defineProperty(exports, "__esModule", { value: true });
7
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":";AAAA;;;GAGG"}
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Structured logger. JSON to stdout. Pretty-print only when TTY + DEBUG set.
3
+ */
4
+ import pino from "pino";
5
+ export declare const logger: pino.Logger<never, boolean>;
6
+ export type Logger = typeof logger;