@vijaypjavvadi/bdd2pw 2.2.7 → 3.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +210 -0
- package/dist/emitters/facade.d.ts +20 -0
- package/dist/emitters/facade.js +68 -6
- package/dist/emitters/facade.js.map +1 -1
- package/dist/index.js +14 -2
- package/dist/index.js.map +1 -1
- package/dist/llm/anthropicClient.js +9 -0
- package/dist/llm/anthropicClient.js.map +1 -1
- package/dist/transformers/apiRules.d.ts +40 -0
- package/dist/transformers/apiRules.js +335 -0
- package/dist/transformers/apiRules.js.map +1 -0
- package/dist/transformers/stepMatcher.js +16 -0
- package/dist/transformers/stepMatcher.js.map +1 -1
- package/dist/transformers/stepNormalizer.js +11 -0
- package/dist/transformers/stepNormalizer.js.map +1 -1
- package/dist/transformers/visibilityRules.d.ts +37 -0
- package/dist/transformers/visibilityRules.js +197 -0
- package/dist/transformers/visibilityRules.js.map +1 -0
- package/dist/types.d.ts +31 -0
- package/package.json +2 -2
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* v3.1.0 — catch-all visibility rules.
|
|
4
|
+
*
|
|
5
|
+
* Why this exists: TestForge AI's handoff report (Issue 1, P0) showed
|
|
6
|
+
* that steps like "the user's name or profile indicator is visible in
|
|
7
|
+
* the UI" were falling through every existing visibility rule (which
|
|
8
|
+
* require specific noun-phrase shapes like "a Logout button" or "an
|
|
9
|
+
* error message") and ending up in the URL-slug rules, where the
|
|
10
|
+
* prose was tokenised into a regex that never matches a real URL.
|
|
11
|
+
* Result: silent always-failing tests that look like product bugs.
|
|
12
|
+
*
|
|
13
|
+
* These rules slot in BEFORE all URL-slug rules so any
|
|
14
|
+
* "is visible / displayed / shown / appears" phrasing is intercepted
|
|
15
|
+
* deterministically. The captured noun phrase is matched against the
|
|
16
|
+
* POM's fields (fuzzy / case-insensitive contains); if a field matches,
|
|
17
|
+
* we emit `expect(<pomVar>.<field>).toBeVisible()`. If nothing matches,
|
|
18
|
+
* we emit a clean `// TODO bdd2pw: ambiguous visibility step` warning
|
|
19
|
+
* — never a URL regex.
|
|
20
|
+
*
|
|
21
|
+
* Coverage:
|
|
22
|
+
* - "<noun> is visible (in <where>)?"
|
|
23
|
+
* - "<noun> is displayed (in <where>)?"
|
|
24
|
+
* - "<noun> is shown (in <where>)?"
|
|
25
|
+
* - "<noun> appears (on screen|in <where>)?"
|
|
26
|
+
* - "<noun> should be visible / displayed / shown"
|
|
27
|
+
* - "<noun> is enabled / clickable" → toBeEnabled()
|
|
28
|
+
*
|
|
29
|
+
* The noun can be arbitrary prose (apostrophes, "or", multiple words);
|
|
30
|
+
* we don't try to slugify it into a locator.
|
|
31
|
+
*/
|
|
32
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
33
|
+
exports.VISIBILITY_RULES = void 0;
|
|
34
|
+
/** Words that, if they appear inside a captured noun, suggest the
|
|
35
|
+
* step refers to a navigation / URL claim rather than a UI element.
|
|
36
|
+
* When found, we decline the visibility rule so URL rules can have
|
|
37
|
+
* another chance. */
|
|
38
|
+
const URL_HINT_WORDS = new Set([
|
|
39
|
+
"url",
|
|
40
|
+
"path",
|
|
41
|
+
"endpoint",
|
|
42
|
+
"page",
|
|
43
|
+
"route",
|
|
44
|
+
]);
|
|
45
|
+
/**
|
|
46
|
+
* Role hints THAT N6 SPECIFICALLY HANDLES. When the captured noun
|
|
47
|
+
* phrase ends with one of these, the existing N6 rule synthesises a
|
|
48
|
+
* cross-role locator ("a Logout button is visible" →
|
|
49
|
+
* `getByRole('button', { name: /Logout/i })` with link fallback). We
|
|
50
|
+
* decline our visibility rule so N6 wins.
|
|
51
|
+
*
|
|
52
|
+
* Kept narrow on purpose: nouns that end in `message`/`banner`/`alert`
|
|
53
|
+
* but DON'T fit N5c's "containing" shape (e.g. "the welcome banner is
|
|
54
|
+
* shown") should still get our TODO treatment, not be left orphaned.
|
|
55
|
+
*/
|
|
56
|
+
const N6_ROLE_TAIL = /\b(?:button|link|icon|element|tab)$/i;
|
|
57
|
+
/**
|
|
58
|
+
* N5c handles "<an? <kind> message|alert|banner> containing '<value>'
|
|
59
|
+
* is displayed/shown" with `toContainText`. Whenever we see a noun that
|
|
60
|
+
* contains the literal word "containing", that's a strong signal it's
|
|
61
|
+
* N5c-shaped and we should decline so N5c can produce the right
|
|
62
|
+
* assertion.
|
|
63
|
+
*/
|
|
64
|
+
const N5C_LIKE = /\bcontaining\b/i;
|
|
65
|
+
/**
|
|
66
|
+
* N7 handles negation-prefixed visibility steps ("No success message is
|
|
67
|
+
* shown", "No 'Log out' button appears", "No errors are displayed").
|
|
68
|
+
* The N7 pattern requires the step to START with "No ", so we decline
|
|
69
|
+
* our visibility rule whenever the captured noun begins with that
|
|
70
|
+
* negation prefix.
|
|
71
|
+
*
|
|
72
|
+
* The case-insensitive match also catches "no errors are displayed"
|
|
73
|
+
* just in case a future test exercises lowercase negation.
|
|
74
|
+
*/
|
|
75
|
+
const N7_NEGATION_PREFIX = /^no\s+/i;
|
|
76
|
+
/**
|
|
77
|
+
* Decline this rule when the noun:
|
|
78
|
+
* 1. is the empty string,
|
|
79
|
+
* 2. contains URL hint words ("url", "page", "route", …),
|
|
80
|
+
* 3. ends with one of N6's role hints (button|link|icon|element|tab),
|
|
81
|
+
* 4. looks like an N5c "containing" phrase,
|
|
82
|
+
* 5. starts with the N7 negation prefix "No ".
|
|
83
|
+
*/
|
|
84
|
+
function shouldDeclineVisibilityRule(noun) {
|
|
85
|
+
const normalised = normaliseNoun(noun);
|
|
86
|
+
if (!normalised)
|
|
87
|
+
return true;
|
|
88
|
+
const tokens = normalised.split(/\s+/);
|
|
89
|
+
if (tokens.some((t) => URL_HINT_WORDS.has(t)))
|
|
90
|
+
return true;
|
|
91
|
+
if (N6_ROLE_TAIL.test(normalised))
|
|
92
|
+
return true;
|
|
93
|
+
if (N5C_LIKE.test(normalised))
|
|
94
|
+
return true;
|
|
95
|
+
if (N7_NEGATION_PREFIX.test(normalised))
|
|
96
|
+
return true;
|
|
97
|
+
return false;
|
|
98
|
+
}
|
|
99
|
+
/** Lowercase the noun phrase and squash whitespace for matching. */
|
|
100
|
+
function normaliseNoun(s) {
|
|
101
|
+
return s.toLowerCase().replace(/\s+/g, " ").trim();
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Try to find a POM field whose name contains the noun phrase (or vice
|
|
105
|
+
* versa). Returns `<pageVar>.<fieldName>` when a match is found,
|
|
106
|
+
* otherwise undefined.
|
|
107
|
+
*
|
|
108
|
+
* Conservative — only matches when one is a substring of the other,
|
|
109
|
+
* case-insensitive. We never invent locators; if nothing matches the
|
|
110
|
+
* caller emits a TODO.
|
|
111
|
+
*/
|
|
112
|
+
function resolveLocatorFromPom(noun, pom, pageVar) {
|
|
113
|
+
const target = normaliseNoun(noun).replace(/[^a-z0-9]/g, "");
|
|
114
|
+
if (!target)
|
|
115
|
+
return undefined;
|
|
116
|
+
for (const field of pom.fields) {
|
|
117
|
+
const fieldKey = field.fieldName.toLowerCase();
|
|
118
|
+
if (fieldKey.includes(target) || target.includes(fieldKey)) {
|
|
119
|
+
return `${pageVar}.${field.fieldName}`;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
return undefined;
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* Shared build logic for visibility rules. Drives the matcher decision
|
|
126
|
+
* once the regex has captured the noun phrase and chosen the matcher.
|
|
127
|
+
*/
|
|
128
|
+
function buildVisibilityBinding(step, pom, pageVar, noun, matcher) {
|
|
129
|
+
// Decline the rule when the captured noun is shaped for an existing
|
|
130
|
+
// richer rule — URL rules (URL/path/page hints), or N5/N6/N7
|
|
131
|
+
// cross-role rules (role-hint tail). The visibility rules in this
|
|
132
|
+
// module are only meant to catch PROSE noun phrases that no other
|
|
133
|
+
// rule handles. See `shouldDeclineVisibilityRule` for the full list.
|
|
134
|
+
if (shouldDeclineVisibilityRule(noun))
|
|
135
|
+
return null;
|
|
136
|
+
const locator = resolveLocatorFromPom(noun, pom, pageVar);
|
|
137
|
+
if (!locator) {
|
|
138
|
+
// No POM field match — emit a clean TODO. CRUCIALLY, we do NOT
|
|
139
|
+
// fall through to a URL regex; this is the whole point of Issue 1.
|
|
140
|
+
return {
|
|
141
|
+
step,
|
|
142
|
+
warning: `ambiguous visibility step: no POM field matched "${noun.trim()}". ` +
|
|
143
|
+
`Add a locator field to the POM, or hand-edit the spec to point at the ` +
|
|
144
|
+
`right element. (bdd2pw will not synthesise a URL regex from a prose ` +
|
|
145
|
+
`noun — that produced always-failing tests in v3.0.0 and earlier.)`,
|
|
146
|
+
};
|
|
147
|
+
}
|
|
148
|
+
return {
|
|
149
|
+
step,
|
|
150
|
+
assertion: { locator, matcher },
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
exports.VISIBILITY_RULES = [
|
|
154
|
+
// V:01 — "<noun> is visible (in/on <where>)?"
|
|
155
|
+
// Catches the TestForge reproduction:
|
|
156
|
+
// "the user's name or profile indicator is visible in the UI"
|
|
157
|
+
//
|
|
158
|
+
// The noun phrase can contain apostrophes, "or", multiple words. We
|
|
159
|
+
// capture greedily up to ` is visible` and then optionally consume
|
|
160
|
+
// a trailing " in/on <something>" clause.
|
|
161
|
+
{
|
|
162
|
+
pattern: /^(.+?)\s+is\s+visible(?:\s+(?:in|on)\s+the\s+(?:ui|page|screen))?$/i,
|
|
163
|
+
build: (m, step, pom, pageVar) => buildVisibilityBinding(step, pom, pageVar, m[1], "toBeVisible"),
|
|
164
|
+
},
|
|
165
|
+
// V:02 — "<noun> is displayed (in/on <where>)?"
|
|
166
|
+
{
|
|
167
|
+
pattern: /^(.+?)\s+is\s+displayed(?:\s+(?:in|on)\s+the\s+(?:ui|page|screen))?$/i,
|
|
168
|
+
build: (m, step, pom, pageVar) => buildVisibilityBinding(step, pom, pageVar, m[1], "toBeVisible"),
|
|
169
|
+
},
|
|
170
|
+
// V:03 — "<noun> is shown (in/on <where>)?"
|
|
171
|
+
{
|
|
172
|
+
pattern: /^(.+?)\s+is\s+shown(?:\s+(?:in|on)\s+the\s+(?:ui|page|screen))?$/i,
|
|
173
|
+
build: (m, step, pom, pageVar) => buildVisibilityBinding(step, pom, pageVar, m[1], "toBeVisible"),
|
|
174
|
+
},
|
|
175
|
+
// V:04 — "<noun> appears (on screen|in/on the ui/page)?"
|
|
176
|
+
{
|
|
177
|
+
pattern: /^(.+?)\s+appears(?:\s+(?:on\s+screen|in\s+the\s+(?:ui|page)|on\s+the\s+page))?$/i,
|
|
178
|
+
build: (m, step, pom, pageVar) => buildVisibilityBinding(step, pom, pageVar, m[1], "toBeVisible"),
|
|
179
|
+
},
|
|
180
|
+
// V:05 — "<noun> should be visible / displayed / shown"
|
|
181
|
+
{
|
|
182
|
+
pattern: /^(.+?)\s+should\s+be\s+(?:visible|displayed|shown)$/i,
|
|
183
|
+
build: (m, step, pom, pageVar) => buildVisibilityBinding(step, pom, pageVar, m[1], "toBeVisible"),
|
|
184
|
+
},
|
|
185
|
+
// V:06 — negative: "<noun> is (not visible|hidden|not displayed|not shown)"
|
|
186
|
+
{
|
|
187
|
+
pattern: /^(.+?)\s+is\s+(?:not\s+visible|hidden|not\s+displayed|not\s+shown)$/i,
|
|
188
|
+
build: (m, step, pom, pageVar) => buildVisibilityBinding(step, pom, pageVar, m[1], "not.toBeVisible"),
|
|
189
|
+
},
|
|
190
|
+
// V:07 — "<noun> is enabled / clickable"
|
|
191
|
+
{
|
|
192
|
+
pattern: /^(.+?)\s+is\s+(?:enabled|clickable)$/i,
|
|
193
|
+
build: (m, step, pom, pageVar) => buildVisibilityBinding(step, pom, pageVar, m[1], "toBeEnabled"),
|
|
194
|
+
},
|
|
195
|
+
];
|
|
196
|
+
// (end of visibilityRules.ts — v3.1.0)
|
|
197
|
+
//# sourceMappingURL=visibilityRules.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"visibilityRules.js","sourceRoot":"","sources":["../../src/transformers/visibilityRules.ts"],"names":[],"mappings":"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"}
|
package/dist/types.d.ts
CHANGED
|
@@ -88,6 +88,19 @@ export interface StepBinding {
|
|
|
88
88
|
customBody?: string;
|
|
89
89
|
/** Populated when no clean mapping was found. */
|
|
90
90
|
warning?: string;
|
|
91
|
+
/**
|
|
92
|
+
* v3.0.0 — flag set by API-pattern rules (page.request.*). When ANY
|
|
93
|
+
* binding in a scenario is API-flagged, the renderer:
|
|
94
|
+
* 1. Adds `type APIResponse` to the @playwright/test import.
|
|
95
|
+
* 2. Injects `let apiResponse: APIResponse | null = null;` and
|
|
96
|
+
* `let baseUrl: string = process.env.CLOUD_JOB_APP_URL ?? "";`
|
|
97
|
+
* inside the describe block.
|
|
98
|
+
* 3. Prepends `apiResponse = null;` to the test body (per-test reset).
|
|
99
|
+
*
|
|
100
|
+
* UI-only scenarios in the same feature don't get any of that — the
|
|
101
|
+
* flag is per-binding, the emitter aggregates per-scenario.
|
|
102
|
+
*/
|
|
103
|
+
apiContext?: true;
|
|
91
104
|
}
|
|
92
105
|
export interface ReviewItem {
|
|
93
106
|
severity: "info" | "warn" | "error";
|
|
@@ -188,6 +201,24 @@ export interface ScaffoldOptions {
|
|
|
188
201
|
* Default: false (purely additive minor bump).
|
|
189
202
|
*/
|
|
190
203
|
selfHealing?: boolean;
|
|
204
|
+
/**
|
|
205
|
+
* v3.1.0 — opt-in instrumentation. See TestForge handoff Issue 4 and
|
|
206
|
+
* Issue 5.
|
|
207
|
+
*
|
|
208
|
+
* - `stepHooks`: every emitted `test.step` body calls
|
|
209
|
+
* `(globalThis as any).__bdd2pwHooks?.beforeStep?.(testInfo, title)`
|
|
210
|
+
* and `?.afterStep?.(...)`. Consumers wire the hook to do per-step
|
|
211
|
+
* screenshots, custom reporters, artefact uploads, etc.
|
|
212
|
+
* - `stepMarkers`: each `test.step` is bracketed by stable
|
|
213
|
+
* `// bdd2pw:step-open id="NNNN" title="..."` /
|
|
214
|
+
* `// bdd2pw:step-close id="NNNN"` comments. Lets post-processors
|
|
215
|
+
* slice the source without brace counting.
|
|
216
|
+
*
|
|
217
|
+
* Both default to false. Existing UI / API emission paths are
|
|
218
|
+
* byte-stable when these are off.
|
|
219
|
+
*/
|
|
220
|
+
stepHooks?: boolean;
|
|
221
|
+
stepMarkers?: boolean;
|
|
191
222
|
}
|
|
192
223
|
export interface ScaffoldResult {
|
|
193
224
|
filesWritten: string[];
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vijaypjavvadi/bdd2pw",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "3.1.0",
|
|
4
4
|
"description": "Scaffold runnable Playwright TypeScript tests from Gherkin .feature files. Detects existing Page Objects, scans live pages via Microsoft Playwright MCP, emits POMs + specs, ready for execution. CLI + HTTP service. Optional governed LLM fallback (Anthropic / OpenAI / Gemini).",
|
|
5
5
|
"bin": {
|
|
6
6
|
"bdd2pw": "dist/cli.js"
|
|
@@ -59,7 +59,7 @@
|
|
|
59
59
|
"dependencies": {
|
|
60
60
|
"@cucumber/gherkin": "^28.0.0",
|
|
61
61
|
"@cucumber/messages": "^24.1.0",
|
|
62
|
-
"@vijaypjavvadi/pw-emit": "^1.
|
|
62
|
+
"@vijaypjavvadi/pw-emit": "^1.2.0",
|
|
63
63
|
"archiver": "^7.0.1",
|
|
64
64
|
"chalk": "^4.1.2",
|
|
65
65
|
"commander": "^12.1.0",
|